<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Alex van Zyl's Blog]]></title><description><![CDATA[Alex van Zyl's Blog]]></description><link>https://alexvanzyl.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 05 Sep 2026 14:32:47 GMT</lastBuildDate><atom:link href="https://alexvanzyl.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Create a Flash Loan with Uniswap V3]]></title><description><![CDATA[In this article, I will show you how to create a flash loan with Uniswap v3.
Before getting started, I am going to assume you already have node and npm installed and are somewhat familiar with the solidity programming language.
What will you learn fr...]]></description><link>https://alexvanzyl.com/create-a-flash-loan-with-uniswap-v3</link><guid isPermaLink="true">https://alexvanzyl.com/create-a-flash-loan-with-uniswap-v3</guid><category><![CDATA[uniswap]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[defi]]></category><dc:creator><![CDATA[Alex van Zyl]]></dc:creator><pubDate>Fri, 16 Dec 2022 11:20:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671189284536/wsE-rLJha.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I will show you how to create a flash loan with Uniswap v3.</p>
<p>Before getting started, I am going to assume you already have node and npm installed and are somewhat familiar with the solidity programming language.</p>
<p>What will you learn from this article?</p>
<ul>
<li><p>Setting up a hardhat project from scratch</p>
</li>
<li><p>Using Alchemy and Hardhat to fork mainnet</p>
</li>
<li><p>How to leverage Uniswap v3 Flash Swaps to create a flash loan.</p>
</li>
</ul>
<p>Let's get started! Fire up a terminal 🖥️</p>
<h2 id="heading-setting-up-a-hardhat-project">Setting up a hardhat project</h2>
<p>First, create a directory for your project <code>cd</code> into it and use <code>npm init -y</code> to generate a <code>package.json</code> file.</p>
<pre><code class="lang-bash">&gt; mkdir uniswapv3-flashloan
&gt; <span class="hljs-built_in">cd</span> uniswapv3-flashloan
&gt; npm init -y
</code></pre>
<p>Next, go ahead and install Hardhat.</p>
<pre><code class="lang-bash">&gt; npm install --save-dev hardhat
</code></pre>
<p>You can now use <code>npx hardhat</code> to generate a Hardhat project. Following the instructions create a TypeScript project and answer yes to the rest of the questions. This will generate the necessary files and install the required dependencies.</p>
<pre><code class="lang-bash">&gt; npx hardhat      
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     <span class="hljs-string">"88b 888P"</span>  d88<span class="hljs-string">" 888 888 "</span>88b     <span class="hljs-string">"88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "</span>Y888888 888     <span class="hljs-string">"Y88888 888  888 "</span>Y888888  <span class="hljs-string">"Y888

Welcome to Hardhat v2.12.3

✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: · /home/alex/Code/Sandbox/uniswapv3-flashloan
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y</span>
</code></pre>
<p>At this point, you should have a directory structure that looks like this.</p>
<pre><code class="lang-bash">.
├── contracts
├── .gitignore
├── hardhat.config.ts
├── node_modules
├── package.json
├── package-lock.json
├── README.md
├── scripts
├── <span class="hljs-built_in">test</span>
└── tsconfig.json
</code></pre>
<p>If you'd like you can delete the sample code that was generated in the <code>contracts</code> , <code>scripts</code> and <code>test</code> directories.</p>
<p>Great, you now have a TypeScript Hardhat project, ready to go!</p>
<h2 id="heading-forking-minnet">Forking Minnet</h2>
<p>Hardhat allows you to create a fork of mainnet locally. Essentially what this means is you can interact with contracts that are on the mainnet using your local set-up.</p>
<p>In this section, you'll create an application in <a target="_blank" href="https://auth.alchemy.com/?redirectUrl=https%3A%2F%2Fdashboard.alchemy.com%2F">Alchemy</a>, generate an API key and make some modifications to the <code>hardhat.config.ts</code> file.</p>
<h3 id="heading-1-create-an-alchemy-application">1. Create an Alchemy Application</h3>
<p>If you haven't already created an account on Alchemy, go ahead and sign up. Once you have logged in, click the create app button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671183973702/zX3O47y3f.png" alt class="image--center mx-auto" /></p>
<p>Make sure you selected Ethereum for the chain and Mainnet for the network.</p>
<p>Once you have created the application click on View Details and then View Key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671184077554/vf6ZPjWRr.png" alt class="image--center mx-auto" /></p>
<p>Copy the HTTPS URL. Keep this handy you will use it in the section that follows.</p>
<h3 id="heading-2-create-a-env-file">2. Create a .env file</h3>
<p>You wouldn't want to share your API key with the whole world. To avoid this you will create a <code>.env</code> file, this <strong>SHOULD NOT</strong> be committed to version control. Double-check that you have <code>.gitignore</code> a file at the root of your project with the following content.</p>
<pre><code class="lang-plaintext">node_modules
.env
coverage
coverage.json
typechain
typechain-types

# Hardhat files
cache
artifacts
</code></pre>
<p>Create a <code>.env</code> file with only one environment variable <code>MAINNET_URL</code> and paste the URL that you copied from Alchemy in the previous section. You should end up with something like this.</p>
<pre><code class="lang-plaintext">MAINNET_URL=https://eth-mainnet.g.alchemy.com/v2/&lt;api_key&gt;
</code></pre>
<p>Remember to replace <code>&lt;api_key&gt;</code> with the actual API key, you copied before.</p>
<h3 id="heading-3-modify-the-hardhatconfigts-file">3. Modify the <code>hardhat.config.ts</code> file</h3>
<p>To read environment variables from a <code>.env</code> file you can make use of a package called <a target="_blank" href="https://github.com/motdotla/dotenv">dotenv</a>. Go ahead and install that package now.</p>
<pre><code class="lang-bash">&gt; npm install --save-dev dotenv
</code></pre>
<p>Finally, modify the <code>hardhat.config.ts</code> file to look like this.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;

<span class="hljs-keyword">import</span> { HardhatUserConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"hardhat/config"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@nomicfoundation/hardhat-toolbox"</span>;

dotenv.config();

<span class="hljs-keyword">const</span> config: HardhatUserConfig = {
  solidity: <span class="hljs-string">"0.8.17"</span>,
  networks: {
    hardhat: {
      forking: {
        url: process.env.MAINNET_URL || <span class="hljs-string">""</span>,
      },
    },
  },
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> config;
</code></pre>
<p>And voila you now have a Hardhat project that forks mainnet!</p>
<h2 id="heading-creating-a-flash-loan-contract">Creating a Flash Loan Contract</h2>
<p>Finally, it's time for the juicy parts! In this section, you will build the contract that will perform the flash loan on Uniswap V3.</p>
<p>Before diving into creating the contract a little bit of theory might help you better understand what we doing here. For each token pair, Uniswap has what is called a liquidity pool. For example, let's say you have ETH/USDC token pair, you could think of the liquidity pool as one big vault that is split into two sections. One section would hold X amount of ETH while the other section would hold the equivalent amount in USDC.</p>
<p>When you create a flash loan on Uniswap essentially what you doing is taking liquidity out of the pool and putting it back in a single transaction. Using the vault example above, you borrow some ETH or USDC from the vault and immediately put it back before anyone notices.</p>
<p>You could also borrow both ETH and USDC in the same transaction, as long as you have enough of the same token to pay the cost of the fees.</p>
<p>So as promised now for the juice!</p>
<p>The complete contract including the supporting interface and libraries will look like the below.</p>
<pre><code class="lang-solidity"><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.17;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"hardhat/console.sol"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"./interfaces/IERC20.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./interfaces/IUniswapV3Pool.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./libraries/PoolAddress.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Flashloan</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">constant</span> FACTORY <span class="hljs-operator">=</span>
        <span class="hljs-number">0x1F98431c8aD98523631AE4a59f267346ea31F984</span>;

    <span class="hljs-keyword">struct</span> <span class="hljs-title">FlashCallbackData</span> {
        <span class="hljs-keyword">uint256</span> amount0;
        <span class="hljs-keyword">uint256</span> amount1;
        <span class="hljs-keyword">address</span> caller;
    }

    IERC20 <span class="hljs-keyword">private</span> <span class="hljs-keyword">immutable</span> token0;
    IERC20 <span class="hljs-keyword">private</span> <span class="hljs-keyword">immutable</span> token1;

    IUniswapV3Pool <span class="hljs-keyword">private</span> <span class="hljs-keyword">immutable</span> pool;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _token0,
        <span class="hljs-keyword">address</span> _token1,
        <span class="hljs-keyword">uint24</span> _fee
    </span>) </span>{
        token0 <span class="hljs-operator">=</span> IERC20(_token0);
        token1 <span class="hljs-operator">=</span> IERC20(_token1);
        pool <span class="hljs-operator">=</span> IUniswapV3Pool(getPool(_token0, _token1, _fee));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPool</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _token0,
        <span class="hljs-keyword">address</span> _token1,
        <span class="hljs-keyword">uint24</span> _fee
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        PoolAddress.PoolKey <span class="hljs-keyword">memory</span> poolKey <span class="hljs-operator">=</span> PoolAddress.getPoolKey(
            _token0,
            _token1,
            _fee
        );
        <span class="hljs-keyword">return</span> PoolAddress.computeAddress(FACTORY, poolKey);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flash</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount0, <span class="hljs-keyword">uint256</span> amount1</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
            FlashCallbackData({
                amount0: amount0,
                amount1: amount1,
                caller: <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>
            })
        );
        IUniswapV3Pool(pool).flash(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount0, amount1, data);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uniswapV3FlashCallback</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> fee0,
        <span class="hljs-keyword">uint256</span> fee1,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(pool), <span class="hljs-string">"not authorized"</span>);

        FlashCallbackData <span class="hljs-keyword">memory</span> decoded <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(
            data,
            (FlashCallbackData)
        );

        <span class="hljs-comment">// Do your abitrage below...</span>

        console.log(token0.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));
        console.log(token1.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));

        <span class="hljs-comment">// Repay borrow</span>
        <span class="hljs-keyword">if</span> (fee0 <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            token0.transferFrom(decoded.caller, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), fee0);
            token0.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(pool), decoded.amount0 <span class="hljs-operator">+</span> fee0);
        }
        <span class="hljs-keyword">if</span> (fee1 <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            token1.transferFrom(decoded.caller, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), fee1);
            token1.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(pool), decoded.amount1 <span class="hljs-operator">+</span> fee1);
        }
    }
}
</code></pre>
<p>Inside the <code>contracts</code> directory create a new file called <code>Flashloan.sol</code> and copy the above contents into it. While you are at it go ahead and create two new directories called <code>interfaces</code> and <code>libraries</code> you will make use of this shortly.</p>
<p>Let's walk through the important parts of the contract.</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">address</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">constant</span> FACTORY <span class="hljs-operator">=</span> <span class="hljs-number">0x1F98431c8aD98523631AE4a59f267346ea31F984</span>;
</code></pre>
<p>This is the address of the UniswapV3Factory contract. You can find a complete list of deployed contracts and their address in the Uniswap documentation <a target="_blank" href="https://docs.uniswap.org/contracts/v3/reference/deployments">here</a>.</p>
<p>Moving on to the constructor.</p>
<pre><code class="lang-solidity">    ...
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _token0,
        <span class="hljs-keyword">address</span> _token1,
        <span class="hljs-keyword">uint24</span> _fee
    </span>) </span>{
        token0 <span class="hljs-operator">=</span> IERC20(_token0);
        token1 <span class="hljs-operator">=</span> IERC20(_token1);
        pool <span class="hljs-operator">=</span> IUniswapV3Pool(getPool(_token0, _token1, _fee));
    }
    ...
</code></pre>
<p>When deploying the contract the constructor takes three parameters. <code>_token0</code> and <code>_token1</code> are the addresses of the tokens that will form a pair, as mentioned at the beginning of this section. The <code>_fee</code> refers to the swapping fee of the token pair pool. As of Uniswap v3, there are four fee levels 0.01%, 0.05%, 0.30%, and 1%.</p>
<p>You will also notice the use of two interfaces in the constructor <code>IERC20</code> and <code>IUniswapV3Pool</code>.</p>
<p>Inside of the <code>interfaces</code> directory you created previously two new files, <code>IERC20.sol</code> and <code>IUniswapV3Pool.sol</code>.</p>
<p>The <code>IERC20</code> interface will allow you to interact with all tokens that have implemented the ERC-20 standard. Copy the below content into your <code>IER20.sol</code> file.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-comment">// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)</span>

<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-comment">/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IERC20</span> </span>{
    <span class="hljs-comment">/**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> <span class="hljs-keyword">from</span>, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> to, <span class="hljs-keyword">uint256</span> value</span>)</span>;

    <span class="hljs-comment">/**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Approval</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> owner,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> spender,
        <span class="hljs-keyword">uint256</span> value
    </span>)</span>;

    <span class="hljs-comment">/**
     * @dev Returns the amount of tokens in existence.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalSupply</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;

    <span class="hljs-comment">/**
     * @dev Returns the amount of tokens owned by `account`.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">balanceOf</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;

    <span class="hljs-comment">/**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;

    <span class="hljs-comment">/**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">allowance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> owner, <span class="hljs-keyword">address</span> spender</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;

    <span class="hljs-comment">/**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> spender, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;

    <span class="hljs-comment">/**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;
}
</code></pre>
<p><a target="_blank" href="https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/token/ERC20/IERC20.sol">source</a></p>
<p>Similar to the <code>IERC20</code> interface, the <code>IUniswapV3Pool</code> interface is used to interact with the<code>UniswapV3Pool</code> contract. You can find a reference to this on Uniswaps' GitHub repository <a target="_blank" href="https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Pool.sol">here</a>. However, the only action you should be interested in is the <code>flash</code> function, which is what you will use to perform the flash loan. A more simplified version of this interface would look like this.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: GPL-2.0-or-later</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> &gt;=0.5.0;</span>

<span class="hljs-comment">/**
 * @dev Simplified IUniswapV3Pool interface with only the flash action.
 */</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IUniswapV3Pool</span> </span>{
    <span class="hljs-comment">/// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback</span>
    <span class="hljs-comment">/// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback</span>
    <span class="hljs-comment">/// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling</span>
    <span class="hljs-comment">/// with 0 amount{0,1} and sending the donation amount(s) from the callback</span>
    <span class="hljs-comment">/// @param recipient The address which will receive the token0 and token1 amounts</span>
    <span class="hljs-comment">/// @param amount0 The amount of token0 to send</span>
    <span class="hljs-comment">/// @param amount1 The amount of token1 to send</span>
    <span class="hljs-comment">/// @param data Any data to be passed through to the callback</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flash</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> recipient,
        <span class="hljs-keyword">uint256</span> amount0,
        <span class="hljs-keyword">uint256</span> amount1,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}
</code></pre>
<p>Go ahead and copy the content above into your <code>IUniswapV3Pool.sol</code> file.</p>
<p>The <code>getPool</code> function is responsible for calculating the token pair pool address.</p>
<pre><code class="lang-solidity">    ...
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPool</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _token0,
        <span class="hljs-keyword">address</span> _token1,
        <span class="hljs-keyword">uint24</span> _fee
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        PoolAddress.PoolKey <span class="hljs-keyword">memory</span> poolKey <span class="hljs-operator">=</span> PoolAddress.getPoolKey(
            _token0,
            _token1,
            _fee
        );
        <span class="hljs-keyword">return</span> PoolAddress.computeAddress(FACTORY, poolKey);
    }
    ...
</code></pre>
<p>To do this you need to make use of the <code>PoolAddress</code> library provided by Uniswap. Inside the <code>libraries</code> directory, create a new file called <code>PoolAddress.sol</code>. Copy the content below into the file you just creates.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// SPDX-License-Identifier: GPL-2.0-or-later</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> &gt;=0.5.0;</span>

<span class="hljs-comment">/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee</span>
<span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">PoolAddress</span> </span>{
    <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">internal</span> <span class="hljs-keyword">constant</span> POOL_INIT_CODE_HASH <span class="hljs-operator">=</span> <span class="hljs-number">0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54</span>;

    <span class="hljs-comment">/// @notice The identifying key of the pool</span>
    <span class="hljs-keyword">struct</span> <span class="hljs-title">PoolKey</span> {
        <span class="hljs-keyword">address</span> token0;
        <span class="hljs-keyword">address</span> token1;
        <span class="hljs-keyword">uint24</span> fee;
    }

    <span class="hljs-comment">/// @notice Returns PoolKey: the ordered tokens with the matched fee levels</span>
    <span class="hljs-comment">/// @param tokenA The first token of a pool, unsorted</span>
    <span class="hljs-comment">/// @param tokenB The second token of a pool, unsorted</span>
    <span class="hljs-comment">/// @param fee The fee level of the pool</span>
    <span class="hljs-comment">/// @return Poolkey The pool details with ordered token0 and token1 assignments</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPoolKey</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> tokenA,
        <span class="hljs-keyword">address</span> tokenB,
        <span class="hljs-keyword">uint24</span> fee
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">PoolKey <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">if</span> (tokenA <span class="hljs-operator">&gt;</span> tokenB) (tokenA, tokenB) <span class="hljs-operator">=</span> (tokenB, tokenA);
        <span class="hljs-keyword">return</span> PoolKey({token0: tokenA, token1: tokenB, fee: fee});
    }

    <span class="hljs-comment">/// @notice Deterministically computes the pool address given the factory and PoolKey</span>
    <span class="hljs-comment">/// @param factory The Uniswap V3 factory contract address</span>
    <span class="hljs-comment">/// @param key The PoolKey</span>
    <span class="hljs-comment">/// @return pool The contract address of the V3 pool</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeAddress</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> factory, PoolKey <span class="hljs-keyword">memory</span> key</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span> pool</span>) </span>{
        <span class="hljs-built_in">require</span>(key.token0 <span class="hljs-operator">&lt;</span> key.token1);
        pool <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(
            <span class="hljs-keyword">uint160</span>(
                <span class="hljs-keyword">uint256</span>(
                    <span class="hljs-built_in">keccak256</span>(
                        <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
                            <span class="hljs-string">hex'ff'</span>,
                            factory,
                            <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(key.token0, key.token1, key.fee)),
                            POOL_INIT_CODE_HASH
                        )
                    )
                )
            )
        );
    }
}
</code></pre>
<p><a target="_blank" href="https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol"><em>source</em></a><em>.</em></p>
<blockquote>
<p>⚠️The <code>computeAddress</code> function in the above code is different than that of the source. I had to further wrap the hash with <code>uint160</code> to get the contract to correctly compile. Further info can be found <a target="_blank" href="https://github.com/Uniswap/v3-periphery/issues/1">here</a>.</p>
</blockquote>
<p>Next is the <code>flash</code> function, this is the actual function that creates the flash loan.</p>
<pre><code class="lang-solidity">    ...
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flash</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount0, <span class="hljs-keyword">uint256</span> amount1</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
            FlashCallbackData({
                amount0: amount0,
                amount1: amount1,
                caller: <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>
            })
        );
        IUniswapV3Pool(pool).flash(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount0, amount1, data);
    }
    ...
</code></pre>
<p>The <code>amount0</code> and <code>amount1</code> correspond directly to the amount of <code>token0</code> and/or <code>token1</code> you wish to borrow. Essentially all this function does is call <code>flash</code> on the token pair pool.</p>
<p>To successfully get the amount you requested in your flash loan your contract must include a callback function name <code>uniswapV3FlashCallback</code> . This leads us to the final part of the contract.</p>
<pre><code class="lang-solidity">    ...
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uniswapV3FlashCallback</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> fee0,
        <span class="hljs-keyword">uint256</span> fee1,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(pool), <span class="hljs-string">"not authorized"</span>);

        FlashCallbackData <span class="hljs-keyword">memory</span> decoded <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(
            data,
            (FlashCallbackData)
        );

        <span class="hljs-comment">// Do your abitrage below...</span>

        console.log(token0.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));
        console.log(token1.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)));

        <span class="hljs-comment">// Repay borrow</span>
        <span class="hljs-keyword">if</span> (fee0 <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            token0.transferFrom(decoded.caller, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), fee0);
            token0.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(pool), decoded.amount0 <span class="hljs-operator">+</span> fee0);
        }
        <span class="hljs-keyword">if</span> (fee1 <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            token1.transferFrom(decoded.caller, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), fee1);
            token1.<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(pool), decoded.amount1 <span class="hljs-operator">+</span> fee1);
        }
    }
    ...
</code></pre>
<p>At this point, you are free to do what you want with the borrowed amount. Maybe leverage your position while yield farming or taking advantage of an arbitrage opportunity. As long as you cover the gas fees and pay back the amount borrowed in a single transaction.</p>
<p>As a final step to see that everything works you going to create a script that deploys the contract and executes the <code>flash</code> function locally.</p>
<p>Inside the <code>scripts</code> directory, create a file and call it <code>flashloan.ts</code> with the below contents.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"hardhat"</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> WETH_ADDRESS = <span class="hljs-string">"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"</span>;
  <span class="hljs-keyword">const</span> USDC_ADDRESS = <span class="hljs-string">"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"</span>;
  <span class="hljs-keyword">const</span> POOL_FEE = <span class="hljs-number">3000</span>; <span class="hljs-comment">// 0.30% tier</span>

  <span class="hljs-comment">// Need this to convert ETH into WETH (Wrapped Ether) to cover the fees</span>
  <span class="hljs-keyword">const</span> weth = <span class="hljs-keyword">await</span> ethers.getContractAt(<span class="hljs-string">"IWETH"</span>, WETH_ADDRESS);

  <span class="hljs-comment">// Deploy Flashloan contract</span>
  <span class="hljs-keyword">const</span> Flashloan = <span class="hljs-keyword">await</span> ethers.getContractFactory(<span class="hljs-string">"Flashloan"</span>);
  <span class="hljs-keyword">const</span> flashloan = <span class="hljs-keyword">await</span> Flashloan.deploy(
    USDC_ADDRESS,
    WETH_ADDRESS,
    POOL_FEE
  );

  <span class="hljs-comment">// Get some WETH to cover fee and approve Flashloan contract to use it.</span>
  <span class="hljs-comment">// Fee: 1 ETH * 0.3% = 0.003 ETH</span>
  <span class="hljs-keyword">await</span> weth.deposit({ value: ethers.utils.parseEther(<span class="hljs-string">"0.003"</span>) });
  <span class="hljs-keyword">await</span> weth.approve(flashloan.address, ethers.utils.parseEther(<span class="hljs-string">"0.003"</span>));

  <span class="hljs-comment">// Execute flashloan to borrow 1 ETH.</span>
  <span class="hljs-keyword">await</span> flashloan.flash(<span class="hljs-number">0</span>, ethers.utils.parseEther(<span class="hljs-string">"1"</span>));
}

<span class="hljs-comment">// We recommend this pattern to be able to use async/await everywhere</span>
<span class="hljs-comment">// and properly handle errors.</span>
main().catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.error(error);
  process.exitCode = <span class="hljs-number">1</span>;
});
</code></pre>
<p>Open up your terminal <code>cd</code> to the project root and run <code>npx hardhat run scripts/flashloan.ts</code>. With any luck, you should see the console logs from within the contract.</p>
<pre><code class="lang-bash">❯ npx hardhat run scripts/flashloan.ts          
0
1000000000000000000
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Give yourself a hand 👏 for making it this far, you just created a flash loan contract using Uniswap v3!</p>
<p>As a quick recap, you created a Hardhat project, set things up to fork mainnet and finally deployed and executed your contract locally.</p>
<p>Happy hacking!</p>
<p>If you enjoyed reading this article and would like to stay tuned for more, or just want to connect, follow me on Twitter <a target="_blank" href="https://twitter.com/alexvanzyl">@alexvanzyl</a></p>
]]></content:encoded></item><item><title><![CDATA[FastAPI: Simple application structure from scratch - Part 3]]></title><description><![CDATA[In the third part of this series, we will containerize our application using Docker.
Series Content :book:

Part 1: Laying the foundation  
Part 2: Migrations
Part 3: Dockerize (this post)

Prerequisites
You will need to install:

Docker; and
Docker ...]]></description><link>https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-3</link><guid isPermaLink="true">https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-3</guid><category><![CDATA[Python]]></category><dc:creator><![CDATA[Alex van Zyl]]></dc:creator><pubDate>Sat, 30 May 2020 18:06:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643287813833/oNHdYsYL0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the third part of this series, we will containerize our application using Docker.</p>
<h2 id="heading-series-content-book">Series Content :book:</h2>
<ul>
<li><a target="_blank" href="https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch">Part 1</a>: Laying the foundation  </li>
<li><a target="_blank" href="https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-2">Part 2</a>: Migrations</li>
<li>Part 3: Dockerize (this post)</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You will need to install:</p>
<ul>
<li><a target="_blank" href="https://docs.docker.com/docker-for-windows/install/">Docker</a>; and</li>
<li><a target="_blank" href="https://docs.docker.com/compose/install/">Docker Compose</a></li>
</ul>
<h2 id="heading-what-we-will-cover-in-this-post-memo">What we will cover in this post? :memo:</h2>
<ul>
<li>Create a Dockerfile for our application</li>
<li>Create Docker Compose files</li>
<li>Update <code>.env</code> and <code>config.py</code></li>
<li>Create a <code>prestart.sh</code> file to run migrations</li>
</ul>
<h2 id="heading-create-a-dockerfile-for-our-application">Create a Dockerfile for our application</h2>
<p>Our Dockerfile will contain the details of how docker should build our server image. This is where our FastAPI application will run.</p>
<p><code>Dockerfile</code></p>
<pre><code class="lang-Dockerfile">FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8

WORKDIR /app/

# Install Poetry
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python &amp;&amp; \
    cd /usr/local/bin &amp;&amp; \
    ln -s /opt/poetry/bin/poetry &amp;&amp; \
    poetry config virtualenvs.create false

# Copy poetry.lock* in case it doesn't exist in the repo
COPY ./pyproject.toml ./poetry.lock* /app/

# Allow installing dev dependencies to run tests
ARG INSTALL_DEV=false
RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --no-dev ; fi"

# For development, Jupyter remote kernel, Hydrogen
# Using inside the container:
# jupyter lab --ip=0.0.0.0 --allow-root --NotebookApp.custom_display_url=http://127.0.0.1:8888
ARG INSTALL_JUPYTER=false
RUN bash -c "if [ $INSTALL_JUPYTER == 'true' ] ; then pip install jupyterlab ; fi"

COPY . /app
ENV PYTHONPATH=/app
</code></pre>
<blockquote>
<p><strong>Info</strong>: The base image we using is one created by the author of FastAPI, Sebastián Ramírez. See the repository on GitHub <a target="_blank" href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker">here</a>.</p>
</blockquote>
<p>The comments in the Dockerfile provide an explanation into what happens when the image gets built. By including <a target="_blank" href="https://github.com/jupyterlab/jupyterlab">JupyterLab</a>, this gives us the ability to spin up a notebook and experiment with ideas while having full access to our FastAPI application.</p>
<p>Since we copy the whole context of our project into the image, it's usually a good idea to create a <code>.dockerignore</code> file. This will prevent adding any unnecessary files to our image as well as sensitive information. This helps to reduce the size of the image and prevent potential security risks.</p>
<p><code>.dockerignore</code></p>
<pre><code>.env
.env.*
.vscode
.idea
<span class="hljs-operator">*</span>.egg-info
.mypy-cache
.cache
.git
.gitignore
docker<span class="hljs-operator">-</span>compose.*
Dockerfile
<span class="hljs-operator">*</span>.md
</code></pre><h2 id="heading-create-docker-compose-files">Create Docker Compose files</h2>
<p>We will use both a <code>docker-compose.yml</code> and <code>docker-compose.override.yml</code> file to orchestrate our services. </p>
<p>The services we will be running are PostgreSQL, pgAdmin (a graphical interface to administer PostgreSQL), and our FastAPI application server.</p>
<p><code>docker-compose.yml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.3"</span>

<span class="hljs-attr">services:</span> 
  <span class="hljs-attr">db:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">postgres:12</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db-data:/var/lib/postgresql/data/pgdata</span>
    <span class="hljs-attr">env_file:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.env</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">PGDATA=/var/lib/postgresql/data/pgdata</span>

  <span class="hljs-attr">pgadmin:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">dpage/pgadmin4</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db</span>
    <span class="hljs-attr">env_file:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.env</span>

  <span class="hljs-attr">server:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">./</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-attr">INSTALL_DEV:</span> <span class="hljs-string">${INSTALL_DEV-false}</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db</span>
    <span class="hljs-attr">env_file:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.env</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">db-data:</span>
</code></pre>
<p>All our services declare the environment variables from reading the <code>.env</code> file. We need to make adjustments to this file, which we will do shortly. For PostgreSQL, we use the official image and for pgAdmin, we use the image created by <a target="_blank" href="https://www.pgadmin.org/download/pgadmin-4-container/">dpage</a>, listed on <a target="_blank" href="https://hub.docker.com/">Docker Hub</a>.</p>
<blockquote>
<p><strong>Info</strong>: You can find the details of the PostgreSQL image <a target="_blank" href="https://hub.docker.com/_/postgres">here</a> and pgAdmin <a target="_blank" href="https://hub.docker.com/r/dpage/pgadmin4/">here</a></p>
</blockquote>
<p>You'll notice we haven't exposed any ports for any of our services. Usually, in production our application would be served behind some sort of proxy, for example, nginx over <code>https</code> protocol. Yet while developing it would be nice to access our services locally. This is where we can make use of the <code>docker-compose.override.yml</code> file. Let's do so now.</p>
<p><code>docker-compose.override.yml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.3"</span>

<span class="hljs-attr">services:</span> 
  <span class="hljs-attr">pgadmin:</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8080:8080"</span>

  <span class="hljs-attr">server:</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8888:8888"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"80:80"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./:/app</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">JUPYTER=jupyter</span> <span class="hljs-string">lab</span> <span class="hljs-string">--ip=0.0.0.0</span> <span class="hljs-string">--allow-root</span> <span class="hljs-string">--NotebookApp.custom_display_url=http://127.0.0.1:8888</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">./</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-attr">INSTALL_DEV:</span> <span class="hljs-string">${INSTALL_DEV-true}</span>
        <span class="hljs-attr">INSTALL_JUPYTER:</span> <span class="hljs-string">${INSTALL_JUPYTER-true}</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">/start-reload.sh</span>
</code></pre>
<p>For pgAdmin it is pretty straightforward we exposing port <code>8080</code>. </p>
<p>The server has a bit more going on though. First, we expose the ports <code>80</code> used to serve our API and <code>8888</code> used to access JupyterLab. Also by default, we install our development dependencies and JupyterLab. For our command, we tell our server that we want to reload after each file change. The changes in files are detected because we create a volume binding between our host machine and the container.</p>
<blockquote>
<p><strong>Info</strong>: The <code>start-reload.sh</code> file is part of the base image. You can see the content of this file <a target="_blank" href="https://github.com/tiangolo/uvicorn-gunicorn-docker/blob/master/docker-images/start-reload.sh">here</a>.</p>
</blockquote>
<h2 id="heading-update-env-and-configpy">Update <code>.env</code> and <code>config.py</code></h2>
<p>We will update our <code>.env</code> file to include more environment variables. It should look like this now.</p>
<p><code>.env</code></p>
<pre><code><span class="hljs-comment"># PostgreSQL</span>
<span class="hljs-attr">POSTGRES_SERVER</span>=db
<span class="hljs-attr">POSTGRES_USER</span>=postgres
<span class="hljs-attr">POSTGRES_PASSWORD</span>=password
<span class="hljs-attr">POSTGRES_DB</span>=app


<span class="hljs-comment"># PgAdmin</span>
<span class="hljs-attr">PGADMIN_DEFAULT_EMAIL</span>=admin@local.host
<span class="hljs-attr">PGADMIN_DEFAULT_PASSWORD</span>=password
<span class="hljs-attr">PGADMIN_LISTEN_PORT</span>=<span class="hljs-number">8080</span>
</code></pre><p>We no longer need to read the <code>.env</code> file from our <code>config.py</code> file. Since the environment variables, are declared inside the running container. We will comment out line 33 in the <code>config.py</code> file.</p>
<p><code>app/config.py</code></p>
<pre><code class="lang-python">...
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        case_sensitive = <span class="hljs-literal">True</span>

        <span class="hljs-comment"># If you want to read environment variables from a .env</span>
        <span class="hljs-comment"># file instead un-comment the below line and create the</span>
        <span class="hljs-comment"># .env file at the root of the project.</span>

        <span class="hljs-comment"># env_file = ".env"</span>


settings = Settings()
</code></pre>
<h2 id="heading-create-prestartsh-file">Create <code>prestart.sh</code> file</h2>
<p>By default, the base image we use for our server looks for a <code>prestart.sh</code> file. This is handy to run scripts before our application starts. We will use it to run our migrations on pre-start.</p>
<p><code>prestart.sh</code></p>
<pre><code class="lang-shell">#! /usr/bin/env bash

# Let the DB start
sleep 10;
# Run migrations
alembic upgrade head
</code></pre>
<p>Let's also give this file execution permissions.</p>
<pre><code class="lang-shell">chmod +x prestart.sh
</code></pre>
<h2 id="heading-conclusion-bulb">Conclusion :bulb:</h2>
<p>Finally, we can now spin up our service using <code>docker-compose up -d</code>. We now can access all our services locally like before.</p>
<pre><code class="lang-shell">docker-compose up -d
</code></pre>
<h3 id="heading-fastapi-swagger-ui">FastAPI Swagger UI</h3>
<p><a target="_blank" href="http://localhost/docs">http://localhost/docs</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643282691848/UmNFe4YWU.png" alt="FastAPI - Swagger UI" /></p>
<h3 id="heading-pgadmin">pgAdmin</h3>
<p><a target="_blank" href="http://localhost:8080">http://localhost:8080</a></p>
<ul>
<li>email: admin@local.host</li>
<li>password: password</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643282693552/VtqPgXPk1.png" alt="pgAdmin Login Page" /></p>
<h3 id="heading-jupyterlab">JupyterLab</h3>
<p>Running JypyterLab requires spinning it up inside our server container. Like so.</p>
<pre><code class="lang-shell">docker-compose exec server bash

# Inside the container
root@a09f3c12bf5e:/app# $JUPYTER
</code></pre>
<p>This will print a link that should look something like this <code>http://127.0.0.1:8888/?token=&lt;token&gt;</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643282695215/n5fn1WcUh.png" alt="JupyterLab Home Page" /></p>
<p>If you have been following since part one, congratulations on making this far! :trophy:</p>
<p>We now have an application that is fully containerized. :whale:</p>
<p>The final code for this post can be found on <a target="_blank" href="https://github.com/alexvanzyl/fastapi-simple-app-example/tree/part-3">GitHub</a>.</p>
<p>If you enjoyed reading this article and would like to stay tuned for more, or just want to connect, follow me on twitter <a target="_blank" href="https://twitter.com/alexvanzyl">@alexvanzyl</a>.</p>
]]></content:encoded></item><item><title><![CDATA[FastAPI: Simple application structure from scratch - Part 2]]></title><description><![CDATA[Continuing where we left off in part one of this series, we will add migrations to our project using Alembic. 
Series Content :book:

Part 1: Laying the foundation  
Part 2: Migrations (this post)
Part 3: Dockerize


Warning: To avoid any issues, mak...]]></description><link>https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-2</link><guid isPermaLink="true">https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-2</guid><category><![CDATA[Python]]></category><dc:creator><![CDATA[Alex van Zyl]]></dc:creator><pubDate>Sun, 24 May 2020 18:00:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643287594366/ldx5Xntqk.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Continuing where we left off in part one of this series, we will add migrations to our project using Alembic. </p>
<h2 id="heading-series-content-book">Series Content :book:</h2>
<ul>
<li><a target="_blank" href="https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch">Part 1</a>: Laying the foundation  </li>
<li>Part 2: Migrations (this post)</li>
<li><a target="_blank" href="https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-3">Part 3</a>: Dockerize</li>
</ul>
<blockquote>
<p><strong>Warning</strong>: To avoid any issues, make sure to drop the posts table from your database and run <code>poetry install</code> again.</p>
</blockquote>
<h2 id="heading-what-we-will-cover-in-this-post-memo">What we will cover in this post? :memo:</h2>
<ul>
<li>What is Alembic?  </li>
<li>Install Alembic </li>
<li>Restructure project to support auto migrations</li>
<li>Create a <code>migrations</code> directory  </li>
<li>Configure Alembic</li>
<li>Generate migration file  </li>
</ul>
<h2 id="heading-what-is-alembic-thinking">What is Alembic? :thinking:</h2>
<p>From Alembic's GitHub <a target="_blank" href="https://github.com/sqlalchemy/alembic">repository</a>.  </p>
<blockquote>
<p>Alembic is a database migrations tool written by the author of <a target="_blank" href="http://www.sqlalchemy.org">SQLAlchemy</a>.  </p>
</blockquote>
<h2 id="heading-install-alembic">Install Alembic</h2>
<p>Since we will be running many commands in our virtual environment, let's switch that shell now.  </p>
<pre><code class="lang-shell">poetry shell
</code></pre>
<p>The first thing we need to do is add Alembic as a dependency.  </p>
<pre><code class="lang-shell">poetry add alembic
</code></pre>
<h2 id="heading-create-a-migrations-directory-filefolder">Create a migrations directory :file_folder:</h2>
<p>Now that we have Alembic installed let's go ahead and generate the <code>migrations</code> directory.  </p>
<pre><code class="lang-shell">alembic init migrations
</code></pre>
<p>After executing the above command a <code>migrations</code> directory and <code>alembic.ini</code> file will be generated. Our new project structure will look like this now.</p>
<pre><code>.
├── alembic.ini
├── app
├── migrations
├── mypy.ini
├── poetry.lock
├── pyproject.toml
├── README.md
└── tests
</code></pre><p>The <code>alembic.ini</code>  file holds the configurations parameter for Alembic, such as the path to our migration scripts. Let's also go over the content of the <code>migrations</code> directroy.</p>
<pre><code>migrations
├── env.py
├── README
├── script.py.mako
└── versions
</code></pre><p>The <code>env.py</code> file reads the configurations from the <code>alembic.ini</code> and handles running the migration scripts. We can also edit this file and tweak to our needs, something we will do later.</p>
<p>The <code>README</code> file can be used to add informative details about our migrations. We will leave this file as-is is for now.</p>
<p>The <code>script.py.mako</code> file is a  <a target="_blank" href="https://www.makotemplates.org/">Mako</a>  template file. Alembic uses this template when generating revision files  (migration scripts).</p>
<p>Finally, we have the <code>versions</code> directory this is where all our revision files will live.</p>
<h2 id="heading-restructure-project-to-support-auto-migrations">Restructure project to support auto migrations</h2>
<p>In order for us to take advantage of Alembics <a target="_blank" href="https://alembic.sqlalchemy.org/en/latest/autogenerate.html">auto generated migrations</a> we need to restructure our project and make some minor changes to existing files.   </p>
<p>Let's create a new <code>db</code> directory now.</p>
<pre><code class="lang-shell">mkdir app/db
</code></pre>
<p>We going to add three new files to this directroy, <code>__init__.py</code>, <code>base.py</code> and <code>session.py</code></p>
<pre><code class="lang-shell">touch app/db/__init__.py app/db/base.py app/db/session.py
</code></pre>
<p>What we now have is an empty <code>db</code> package. This will replace the <code>db.py</code> module and we will delete this file later.</p>
<h3 id="heading-edit-initpy-file">Edit <code>__init__.py</code> file.</h3>
<p><code>app/db/__init__.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> .base <span class="hljs-keyword">import</span> Base  <span class="hljs-comment"># noqa</span>

<span class="hljs-comment"># Import all the models, so that the Base class </span>
<span class="hljs-comment"># has them before being imported by Alembic.</span>
<span class="hljs-keyword">from</span> .. <span class="hljs-keyword">import</span> models <span class="hljs-comment"># noqa</span>
</code></pre>
<p>The schema for each table is derived from the attributes specified on each model. We import all our models afterwards to make sure the <code>Base.metadata</code> attribute gets populates with all the table schemas. Alembic makes use of this when using <code>autogenerate</code>.</p>
<blockquote>
<p><strong>Info</strong>: <code>Base.metadata.tables</code> contains a collection of SQLAlchemy <a target="_blank" href="https://docs.sqlalchemy.org/en/13/core/metadata.html#sqlalchemy.schema.Table"><code>Table</code></a> objects. </p>
</blockquote>
<h3 id="heading-edit-basepy-file">Edit <code>base.py</code> file.</h3>
<p>We going to move the original <code>Base</code> class from the <code>db.py</code> module into this file, but we will make a few changes to it. </p>
<p>First, we going to update our <code>Base</code> class to automatically generate the <code>__tablename__</code> attribute for or models. The name will be derived from the model class name. For example, in our case, the <code>Post</code> class would generate a <code>post</code> table. </p>
<p>I prefer my table names to be in plural form, so instead of <code>post</code> it would <code>posts</code>. For this, we can make use of the <a target="_blank" href="https://github.com/jazzband/inflect">inflect.py</a> module. If you share the same preference, then let's install that now quick.</p>
<pre><code class="lang-shell">poetry add inflect
</code></pre>
<p><code>app/db/base.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any    

<span class="hljs-keyword">import</span> inflect     
<span class="hljs-keyword">from</span> sqlalchemy.ext.declarative <span class="hljs-keyword">import</span> as_declarative, declared_attr    

p = inflect.engine()    


<span class="hljs-meta">@as_declarative()    </span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span>:</span>    
    id: Any    
    __name__: str    

    <span class="hljs-comment"># Generate __tablename__ automatically in plural form.   </span>
    <span class="hljs-comment"># i.e 'Post' model will generate table name 'posts'   </span>
<span class="hljs-meta">    @declared_attr    </span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__tablename__</span>(<span class="hljs-params">cls</span>) -&gt; str:</span>    
        <span class="hljs-keyword">return</span> p.plural(cls.__name__.lower())
</code></pre>
<p>The main changes to the <code>Base</code> class are we added a new <code>__name__</code> and declarative <code>__tablename__</code> attribute.</p>
<p>This means we no longer need to declare the <code>__tablename__</code> attribute on our models. Let’s edit the Post model in our models.py file to reflect this change.</p>
<h3 id="heading-update-modelspy-file">Update <code>models.py</code> file</h3>
<p><code>app/models.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> uuid <span class="hljs-keyword">import</span> uuid4

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, String, Text
<span class="hljs-keyword">from</span> sqlalchemy.dialects.postgresql <span class="hljs-keyword">import</span> UUID

<span class="hljs-keyword">from</span> .db.base <span class="hljs-keyword">import</span> Base


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">Base</span>):</span>
    id = Column(UUID(as_uuid=<span class="hljs-literal">True</span>), primary_key=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">True</span>, default=uuid4)
    title = Column(String)
    body = Column(Text)
</code></pre>
<p>We updated our import statement for the <code>Base</code> class and dropped the <code>__tablename__</code> attribute on the <code>Post</code> model.</p>
<h3 id="heading-edit-sessionpy-file">Edit <code>session.py</code> file</h3>
<p>Again we can copy the session details from <code>db.py</code> file into this one.</p>
<p><code>app/db/session.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker

<span class="hljs-keyword">from</span> ..config <span class="hljs-keyword">import</span> settings

engine =  create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=<span class="hljs-literal">True</span>)
SessionLocal =  sessionmaker(autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>, bind=engine)
</code></pre>
<h3 id="heading-update-mainpy-file">Update <code>main.py</code> file</h3>
<p>We need to update our import statements on line 8 and 9. Also, let's comment out line 13 since we will be using migrations.</p>
<p><code>app/main.py</code></p>
<pre><code class="lang-python">...
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> actions, schemas
<span class="hljs-keyword">from</span> .db.session <span class="hljs-keyword">import</span> SessionLocal
...

<span class="hljs-comment"># Create all tables in database.</span>
<span class="hljs-comment"># Comment this out if you using migrations.</span>
<span class="hljs-comment"># models.Base.metadata.create_all(bind=engine)</span>
...
</code></pre>
<p>We can now delete the <code>db.py</code> file.</p>
<pre><code class="lang-shell">rm app/db.py
</code></pre>
<h2 id="heading-configure-alembic-gear">Configure Alembic :gear:</h2>
<h3 id="heading-update-alembicini-file">Update <code>alembic.ini</code> file</h3>
<p>We going to configure our connection details in the <code>env.py</code> file. So let's open up the <code>alembic.ini</code> and comment out the line <code>sqlalchemy.url = driver://user:pass@localhost/dbname</code>, like below. </p>
<p><code>alembic.ini</code></p>
<pre><code><span class="hljs-keyword">...</span>
<span class="hljs-comment"># the output encoding used when revision files</span>
<span class="hljs-comment"># are written from script.py.mako</span>
<span class="hljs-comment"># output_encoding = utf-8</span>

<span class="hljs-comment"># sqlalchemy.url  = driver://user:pass@localhost/dbname</span>
<span class="hljs-keyword">...</span>
</code></pre><h3 id="heading-update-envpy-file">Update <code>env.py</code> file</h3>
<p>Let's configure the <code>env.py</code> file now. </p>
<p><code>migrations/env.py</code> line 21:22</p>
<pre><code class="lang-python">...
<span class="hljs-comment"># add your model's MetaData object here</span>
<span class="hljs-comment"># for 'autogenerate' support</span>
<span class="hljs-comment"># from myapp import mymodel</span>
<span class="hljs-comment"># target_metadata = mymodel.Base.metadata</span>

<span class="hljs-keyword">from</span> app.db <span class="hljs-keyword">import</span> Base <span class="hljs-comment"># noqa</span>
target_metadata = Base.metadata
...
</code></pre>
<blockquote>
<p><strong>Note</strong>: We import the <code>Base</code> class from <code>app.db</code> and not <code>app.db.base</code> for reason explained before. </p>
</blockquote>
<p>Next we will create our own function the will get the URL to our database connection.</p>
<p><code>migrations/env.py</code> line 31:38</p>
<pre><code class="lang-python">...
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_url</span>():</span>
    <span class="hljs-keyword">from</span> app.config <span class="hljs-keyword">import</span> settings

    user = settings.POSTGRES_USER
    password = settings.POSTGRES_PASSWORD
    server = settings.POSTGRES_SERVER
    db = settings.POSTGRES_DB
    <span class="hljs-keyword">return</span> <span class="hljs-string">f"postgresql://<span class="hljs-subst">{user}</span>:<span class="hljs-subst">{password}</span>@<span class="hljs-subst">{server}</span>/<span class="hljs-subst">{db}</span>"</span>
...
</code></pre>
<p>All we are doing here is importing and using our database configuration settings to generate the connection URL.</p>
<p>To make use of this new function we need to update the <code>run_migrations_offline</code> and <code>run_migrations_online</code> functions. Let's do that now.</p>
<p><code>migrations/env.py</code>  line 53 and 72:78</p>
<pre><code class="lang-python">...
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_migrations_offline</span>():</span>
    ...
    url = get_url()
    ...

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_migrations_online</span>():</span>
    <span class="hljs-string">"""Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """</span>
    configuration = config.get_section(config.config_ini_section)
    configuration[<span class="hljs-string">"sqlalchemy.url"</span>] = get_url()
    connectable = engine_from_config(
        configuration,
        prefix=<span class="hljs-string">"sqlalchemy."</span>,
        poolclass=pool.NullPool,
    )
    ...
...
</code></pre>
<p>The final <code>env.py</code> file should like this now.</p>
<p><code>migrations/env.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> logging.config <span class="hljs-keyword">import</span> fileConfig

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> engine_from_config
<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> pool

<span class="hljs-keyword">from</span> alembic <span class="hljs-keyword">import</span> context

<span class="hljs-comment"># this is the Alembic Config object, which provides</span>
<span class="hljs-comment"># access to the values within the .ini file in use.</span>
config = context.config

<span class="hljs-comment"># Interpret the config file for Python logging.</span>
<span class="hljs-comment"># This line sets up loggers basically.</span>
fileConfig(config.config_file_name)

<span class="hljs-comment"># add your model's MetaData object here</span>
<span class="hljs-comment"># for 'autogenerate' support</span>
<span class="hljs-comment"># from myapp import mymodel</span>
<span class="hljs-comment"># target_metadata = mymodel.Base.metadata</span>

<span class="hljs-keyword">from</span> app.db <span class="hljs-keyword">import</span> Base <span class="hljs-comment"># noqa</span>
<span class="hljs-keyword">from</span> app <span class="hljs-keyword">import</span> models <span class="hljs-comment"># noqa</span>
target_metadata = Base.metadata

<span class="hljs-comment"># other values from the config, defined by the needs of env.py,</span>
<span class="hljs-comment"># can be acquired:</span>
<span class="hljs-comment"># my_important_option = config.get_main_option("my_important_option")</span>
<span class="hljs-comment"># ... etc.</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_url</span>():</span>
    <span class="hljs-keyword">from</span> app.config <span class="hljs-keyword">import</span> settings

    user = settings.POSTGRES_USER
    password = settings.POSTGRES_PASSWORD
    server = settings.POSTGRES_SERVER
    db = settings.POSTGRES_DB
    <span class="hljs-keyword">return</span> <span class="hljs-string">f"postgresql://<span class="hljs-subst">{user}</span>:<span class="hljs-subst">{password}</span>@<span class="hljs-subst">{server}</span>/<span class="hljs-subst">{db}</span>"</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_migrations_offline</span>():</span>
    <span class="hljs-string">"""Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """</span>
    url = get_url()
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=<span class="hljs-literal">True</span>,
        dialect_opts={<span class="hljs-string">"paramstyle"</span>: <span class="hljs-string">"named"</span>},
    )

    <span class="hljs-keyword">with</span> context.begin_transaction():
        context.run_migrations()


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_migrations_online</span>():</span>
    <span class="hljs-string">"""Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """</span>
    configuration = config.get_section(config.config_ini_section)
    configuration[<span class="hljs-string">"sqlalchemy.url"</span>] = get_url()
    connectable = engine_from_config(
        configuration,
        prefix=<span class="hljs-string">"sqlalchemy."</span>,
        poolclass=pool.NullPool,
    )

    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix=<span class="hljs-string">"sqlalchemy."</span>,
        poolclass=pool.NullPool,
    )

    <span class="hljs-keyword">with</span> connectable.connect() <span class="hljs-keyword">as</span> connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

        <span class="hljs-keyword">with</span> context.begin_transaction():
            context.run_migrations()


<span class="hljs-keyword">if</span> context.is_offline_mode():
    run_migrations_offline()
<span class="hljs-keyword">else</span>:
    run_migrations_online()
</code></pre>
<h2 id="heading-generate-migration-file-sparkles">Generate migration file :sparkles:</h2>
<p>Finally, at this point we can now generate our first migraion script.</p>
<pre><code class="lang-shell">alembic revision --autogenerate -m "Create posts table"
</code></pre>
<p>This will generate a new migration file in the <code>migrations/versions/</code> directory. In my case it created a file named <code>ee48ba03fe9f_create_posts_table.py</code> with the below content.</p>
<p><code>migrations/versions/ee48ba03fe9f_create_posts_table.py</code></p>
<pre><code class="lang-python"><span class="hljs-string">"""Create posts table

Revision ID: ee48ba03fe9f
Revises: 
Create Date: 2020-05-24 15:39:50.164129

"""</span>
<span class="hljs-keyword">import</span> sqlalchemy <span class="hljs-keyword">as</span> sa
<span class="hljs-keyword">from</span> alembic <span class="hljs-keyword">import</span> op
<span class="hljs-keyword">from</span> sqlalchemy.dialects <span class="hljs-keyword">import</span> postgresql

<span class="hljs-comment"># revision identifiers, used by Alembic.</span>
revision = <span class="hljs-string">"ee48ba03fe9f"</span>
down_revision = <span class="hljs-literal">None</span>
branch_labels = <span class="hljs-literal">None</span>
depends_on = <span class="hljs-literal">None</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upgrade</span>():</span>
    <span class="hljs-comment"># ### commands auto generated by Alembic - please adjust! ###</span>
    op.create_table(
        <span class="hljs-string">"posts"</span>,
        sa.Column(<span class="hljs-string">"id"</span>, postgresql.UUID(as_uuid=<span class="hljs-literal">True</span>), nullable=<span class="hljs-literal">False</span>),
        sa.Column(<span class="hljs-string">"title"</span>, sa.String(), nullable=<span class="hljs-literal">True</span>),
        sa.Column(<span class="hljs-string">"body"</span>, sa.Text(), nullable=<span class="hljs-literal">True</span>),
        sa.PrimaryKeyConstraint(<span class="hljs-string">"id"</span>),
    )
    op.create_index(op.f(<span class="hljs-string">"ix_posts_id"</span>), <span class="hljs-string">"posts"</span>, [<span class="hljs-string">"id"</span>], unique=<span class="hljs-literal">False</span>)
    <span class="hljs-comment"># ### end Alembic commands ###</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">downgrade</span>():</span>
    <span class="hljs-comment"># ### commands auto generated by Alembic - please adjust! ###</span>
    op.drop_index(op.f(<span class="hljs-string">"ix_posts_id"</span>), table_name=<span class="hljs-string">"posts"</span>)
    op.drop_table(<span class="hljs-string">"posts"</span>)
    <span class="hljs-comment"># ### end Alembic commands ###</span>
</code></pre>
<p>To run this migration now all we need to do is run the following command.</p>
<pre><code class="lang-shell">alembic upgrade head
</code></pre>
<p>We can also roll back changes by running the below.</p>
<pre><code class="lang-shell">alembic downgrade head
</code></pre>
<h2 id="heading-conclusion-bulb">Conclusion :bulb:</h2>
<p>Alembic is now in place to manage all our migration scripts. We had to make some minor changes to achieve this and refactored <code>db.py</code> file into multiple files that make up a package.</p>
<p>The final code for this post can be found on <a target="_blank" href="https://github.com/alexvanzyl/fastapi-simple-app-example/tree/part-2">GitHub</a>.  </p>
<p>If you enjoyed reading this article and would like to stay tuned for more, or just want to connect, follow me on twitter <a target="_blank" href="https://twitter.com/alexvanzyl">@alexvanzyl</a>.</p>
]]></content:encoded></item><item><title><![CDATA[FastAPI: Simple application structure from scratch]]></title><description><![CDATA[In this blog post, we will set up a simple FastAPI application from scratch. This can serve as a good starting point for small to medium projects.
Series Content :book:

Part 1: Laying the foundation (this post)
Part 2: Migrations
Part 3: Dockerize

...]]></description><link>https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch</link><guid isPermaLink="true">https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch</guid><category><![CDATA[Python]]></category><dc:creator><![CDATA[Alex van Zyl]]></dc:creator><pubDate>Tue, 19 May 2020 17:55:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643287310115/XfNFzoEcb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog post, we will set up a simple <a target="_blank" href="https://fastapi.tiangolo.com/">FastAPI</a> application from scratch. This can serve as a good starting point for small to medium projects.</p>
<h2 id="heading-series-content-book">Series Content :book:</h2>
<ul>
<li>Part 1: Laying the foundation (this post)</li>
<li><a target="_blank" href="https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-2">Part 2</a>: Migrations</li>
<li><a target="_blank" href="https://alexvanzyl.com/fastapi-simple-application-structure-from-scratch-part-3">Part 3</a>: Dockerize</li>
</ul>
<h2 id="heading-what-will-we-cover-in-this-post-memo">What will we cover in this post? :memo:</h2>
<ul>
<li>Generate a base project with <a target="_blank" href="https://python-poetry.org/">Poetry</a>.</li>
<li>Install <strong>FastAPI</strong>, <strong>SQLAlchemy</strong> and other dependencies.</li>
<li>Create the necessary files that will serve as the base of the application</li>
</ul>
<h2 id="heading-before-getting-started-warning">Before getting started... :warning:</h2>
<p>I am going to make the following assumptions:</p>
<ul>
<li>that you have a basic understanding of Python and Python <a target="_blank" href="https://docs.python.org/3/library/typing.html">types</a>;</li>
<li>that you already have the below installed:<ul>
<li><a target="_blank" href="https://www.python.org/downloads/">Python</a> 3.6+ (I am using Python 3.8)</li>
<li><a target="_blank" href="https://www.postgresql.org/download/">PostgreSQL</a>; and</li>
<li><a target="_blank" href="https://python-poetry.org/docs/#installation">Poetry</a></li>
</ul>
</li>
</ul>
<p>Without further ado, let's get started :slightly_smiling_face:</p>
<h2 id="heading-create-a-base-project-with-poetry-running">Create a base project with Poetry :running:</h2>
<p>Open up a terminal and enter the below command.</p>
<pre><code class="lang-shell">poetry new app
</code></pre>
<p>After running the above command a new directory called <code>app</code> has been created. Let's go ahead and <code>cd</code> into that directory now.</p>
<pre><code class="lang-shell">cd app
</code></pre>
<p>The directory structure should look like the below.</p>
<pre><code>.
├── app
│   └── __init__.py
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_app.py
</code></pre><p>Let's quickly go over what we have here. </p>
<p>The <code>app</code> directory is our main python package. </p>
<p>A directory with a <code>__init__.py</code> file in it is considered a package in Python. Any <code>.py</code> files we add to this directory will be considered modules of this package. Usually, this file is empty but in this case, Poetry has gone ahead and added <code>__version__  =  '0.1.0'</code>.</p>
<p>The <code>pyproject.toml</code> file is where all our dependencies will be added to. Later on, when we start installing our dependencies you will notice a <code>poetry.lock</code> file will be created, more on that later.</p>
<p>The <code>README</code> file can be used to add details about the project or any useful instructions that will help other developers working on the project. Personally, I prefer writing documentation in Markdown over reStructuredText so I will go ahead and rename <code>README.rst</code> to <code>README.md</code>. Feel free to do the same or leave it as is.</p>
<pre><code class="lang-shell">mv README.rst README.md
</code></pre>
<p>Finally, we have our <code>tests</code> directory that contains all the unit tests.</p>
<h2 id="heading-install-fastapi-and-other-dependencies-package">Install FastAPI and other dependencies :package:</h2>
<p>In this section, we will install only the required dependencies to get a basic CRUD ( <strong>C</strong>reate, <strong>R</strong>ead, <strong>U</strong>pdate, <strong>D</strong>elete) application going.</p>
<h3 id="heading-what-we-will-be-installing">What we will be installing?</h3>
<ul>
<li>FastAPI - this goes without saying :slightly_smiling_face:</li>
<li><a target="_blank" href="https://www.sqlalchemy.org/">SQLAlchemy</a>  Object Relational Mapper (ORM)</li>
<li><a target="_blank" href="https://www.psycopg.org/">psycopg2-binary</a> PostgreSQL database adapter</li>
<li><a target="_blank" href="https://www.uvicorn.org/">Uvicorn</a> a lightning-fast ASGI server</li>
</ul>
<pre><code class="lang-shell">poetry add fastapi sqlalchemy psycopg2-binary uvicorn
</code></pre>
<p>We will also install the following development dependencies, mainly to maintain code quality and for testing.</p>
<ul>
<li><a target="_blank" href="https://docs.pytest.org/en/latest/">pytest</a> testing framework</li>
<li><a target="_blank" href="http://mypy-lang.org/">Mypy</a> static type checker for Python</li>
<li><a target="_blank" href="https://github.com/dropbox/sqlalchemy-stubs">sqlalchemy-stubs</a> Mypy plug-in and type stubs for SQLAlchemy</li>
<li><a target="_blank" href="https://flake8.pycqa.org/en/latest/#">Flake8</a> for code linting</li>
<li><a target="_blank" href="https://github.com/myint/autoflake">autoflake</a> removes unused imports and unused variables</li>
<li><a target="_blank" href="https://github.com/timothycrosley/isort">isort</a> sort import statements</li>
<li><a target="_blank" href="https://pypi.org/project/black/">black</a> Python code formatter</li>
</ul>
<pre><code class="lang-shell">poetry add -D mypy sqlalchemy-stubs flake8 autoflake isort black
</code></pre>
<blockquote>
<p>Note: We have not included <code>pytest</code> in the above command as it is usually installed by default when using Poetry to create a new project.</p>
</blockquote>
<p>At this point, nothing has really changed in our directory structure but you will notice that the <code>pyproject.toml</code> file has been updated and a new <code>poetry.lock</code> file has been created. The <code>poetry.lock</code> file locks the installed dependencies to a specific version. This is in particular helpful when multiple developers are working on the same project, to ensure everyone is using the same versions of each package.</p>
<p>To recap our directory structure should look something like this now.</p>
<pre><code>.
├── app
│   └── __init__.py
├── poetry.lock
├── pyproject.toml
├── README.md
└── tests
    ├── __init__.py
    └── test_app.py
</code></pre><h2 id="heading-add-project-files-pagefacingup">Add project files :page_facing_up:</h2>
<p>In this section, we will start adding the files that will make up the base of our application.</p>
<p>The first file we will create is the <code>main.py</code> file, it will serve as the entry point to our application and house all our routes. Let's create this file now under the <code>app</code> package directory.</p>
<h3 id="heading-mainpy">main.py</h3>
<p><code>app/main.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI

app = FastAPI()


<span class="hljs-meta">@app.get("/")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world!"</span>}
</code></pre>
<p>At this point, we actually have a basic application that we can run. If we switch back to our terminal and run the following commands.</p>
<pre><code class="lang-shell">poetry shell
uvicorn app.main:app
</code></pre>
<blockquote>
<p>Tip: If you want the server to reload on file changes you can use the <code>--reload</code> flag, like so <code>uvicorn app.main:app --reload</code></p>
</blockquote>
<p>Now if we head over to a browser and hit <a target="_blank" href="http://127.0.0.1:8000">http://127.0.0.1:8000</a>  we will be greeted with <code>{"message":"Hello world!"}</code>.  FastAPI also gives us API documentation out of the box so if you now navigate to <a target="_blank" href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a> you will now see the Swagger UI. Pretty awesome, right! :metal:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643282706878/mzbWDVOmp.png" alt="FastAPI - Swagger UI" /></p>
<p>We will come back later and update the <code>main.py</code> file but for now, let's hit <code>Ctrl+C</code> in the terminal to stop Uvicorn and continue adding the rest of our files.</p>
<p>Next, let's create the <code>db.py</code> under the same directory. This file will contain our database session and a base class that all models will extend from.</p>
<h3 id="heading-dbpy">db.py</h3>
<p><code>app/db.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine
<span class="hljs-keyword">from</span> sqlalchemy.ext.declarative <span class="hljs-keyword">import</span> as_declarative
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker

<span class="hljs-keyword">from</span> .config <span class="hljs-keyword">import</span> settings

engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=<span class="hljs-literal">True</span>)
SessionLocal = sessionmaker(autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>, bind=engine)


<span class="hljs-meta">@as_declarative()</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span>:</span>
    id: Any
</code></pre>
<blockquote>
<p>Info: You can read more about the <code>sessionmaker</code> function <a target="_blank" href="https://docs.sqlalchemy.org/en/13/orm/session_basics.html">here</a> and <code>as_declarative</code> decorator <a target="_blank" href="https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/api.html#sqlalchemy.ext.declarative.as_declarative">here</a>.</p>
</blockquote>
<p>You may have noticed we import <code>settings</code> from <code>config</code> but we haven't actually created that file yet, so let's do so now.</p>
<h3 id="heading-configpy">config.py</h3>
<p><code>app/config.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any, Dict, Optional

<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseSettings, PostgresDsn, validator


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>(<span class="hljs-params">BaseSettings</span>):</span>
    POSTGRES_SERVER: str
    POSTGRES_USER: str
    POSTGRES_PASSWORD: str
    POSTGRES_DB: str

    SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = <span class="hljs-literal">None</span>

<span class="hljs-meta">    @validator("SQLALCHEMY_DATABASE_URI", pre=True)</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">assemble_db_connection</span>(<span class="hljs-params">cls, v: Optional[str], values: Dict[str, Any]</span>) -&gt; Any:</span>
        <span class="hljs-keyword">if</span> isinstance(v, str):
            <span class="hljs-keyword">return</span> v
        <span class="hljs-keyword">return</span> PostgresDsn.build(
            scheme=<span class="hljs-string">"postgresql"</span>,
            user=values.get(<span class="hljs-string">"POSTGRES_USER"</span>),
            password=values.get(<span class="hljs-string">"POSTGRES_PASSWORD"</span>),
            host=values.get(<span class="hljs-string">"POSTGRES_SERVER"</span>),
            path=<span class="hljs-string">f"/<span class="hljs-subst">{values.get(<span class="hljs-string">'POSTGRES_DB'</span>) <span class="hljs-keyword">or</span>  <span class="hljs-string">''</span>}</span>"</span>,
        )

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        case_sensitive = <span class="hljs-literal">True</span>
        env_file = <span class="hljs-string">".env"</span>


settings = Settings()
</code></pre>
<p>Info: When loading configurations from a <code>.env</code> file the <a target="_blank" href="https://github.com/theskumar/python-dotenv">python-dotenv</a> package is required.</p>
<p>Here we are making use of Pydantic's  <a target="_blank" href="https://pydantic-docs.helpmanual.io/usage/settings/">settings management</a>. By default, the <code>BaseSettings</code> class will try to read the environment variables set at system level using <a target="_blank" href="https://docs.python.org/3/library/os.html#os.environ">os.environ</a>. However in our case instead we are specifying that we would like our environment variables to be read from a <code>.env</code> file. Pydantic relies on the <a target="_blank" href="https://github.com/theskumar/python-dotenv">python-dotenv</a> package to achieve this, let's add it as a dependency now.</p>
<pre><code class="lang-shell">poetry add python-dotenv
</code></pre>
<p>And now we will create the <code>.env</code> file at the root of the project directory.</p>
<h3 id="heading-env">.env</h3>
<p><code>.env</code></p>
<pre><code><span class="hljs-comment"># PostgreSQL</span>
<span class="hljs-attr">POSTGRES_SERVER</span>=localhost
<span class="hljs-attr">POSTGRES_USER</span>=postgres
<span class="hljs-attr">POSTGRES_PASSWORD</span>=password
<span class="hljs-attr">POSTGRES_DB</span>=app
</code></pre><p>Make sure to edit this file to reflect your set up. </p>
<h3 id="heading-gitignore">.gitignore</h3>
<p>Since the <code>.env</code> file can contain sensitive information we wouldn't want to commit this to version control. So now would probably be a good time to add a <code>.gitignore</code> file to our project. We will copy the Python <code>.gitignore</code> template provided by GitHub <a target="_blank" href="https://github.com/github/gitignore/blob/master/Python.gitignore">here</a>.
<code>.gitignore</code></p>
<pre><code class="lang-shell">wget https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore
mv Python.gitignore .gitignore
</code></pre>
<p>Next, we will create a <code>models.py</code> and <code>schemas.py</code> file. </p>
<h3 id="heading-modelspy">models.py</h3>
<p>The <code>models.py</code> file will contain all our models that extend from the SQLAlchemy <code>Base</code> class we defined in <code>db.py</code> We will create that file now with an example <code>User</code> model.</p>
<p><code>app/models.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> uuid <span class="hljs-keyword">import</span> uuid4
<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, String, Text
<span class="hljs-keyword">from</span> sqlalchemy.dialects.postgresql <span class="hljs-keyword">import</span> UUID

<span class="hljs-keyword">from</span> .db <span class="hljs-keyword">import</span> Base


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">Base</span>):</span>
    __tablename__ = <span class="hljs-string">"posts"</span>

    id = Column(UUID(as_uuid=<span class="hljs-literal">True</span>), primary_key=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">True</span>, default=uuid4)
    title = Column(String)
    body = Column(Text)
</code></pre>
<h3 id="heading-schemaspy">schemas.py</h3>
<p>Let's create the <code>schemas.py</code> file now. This file will contain all our <a target="_blank" href="https://pydantic-docs.helpmanual.io/usage/models/">Pydantic models</a>. Under the hood, FastAPI makes use of these models to validate the incoming request body, parse the response body and generate <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/body/#automatic-docs">automatic docs</a> for our API. Really cool, at least I think so!  :ok_hand:</p>
<p><code>app/schemas.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Optional

<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel, UUID4


<span class="hljs-comment"># Shared properties</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostBase</span>(<span class="hljs-params">BaseModel</span>):</span>
    title: Optional[str] = <span class="hljs-literal">None</span>
    body: Optional[str] = <span class="hljs-literal">None</span>


<span class="hljs-comment"># Properties to receive via API on creation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostCreate</span>(<span class="hljs-params">PostBase</span>):</span>
    title: str
    body: str


<span class="hljs-comment"># Properties to receive via API on update</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostUpdate</span>(<span class="hljs-params">PostBase</span>):</span>
    <span class="hljs-keyword">pass</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostInDBBase</span>(<span class="hljs-params">PostBase</span>):</span>
    id: Optional[UUID4] = <span class="hljs-literal">None</span>

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        orm_mode = <span class="hljs-literal">True</span>


<span class="hljs-comment"># Additional properties to return via API</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">PostInDBBase</span>):</span>
    <span class="hljs-keyword">pass</span>


<span class="hljs-comment"># Additional properties stored in DB</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostInDB</span>(<span class="hljs-params">PostInDBBase</span>):</span>
    <span class="hljs-keyword">pass</span>
</code></pre>
<p>The final file we will create for now is the <code>actions.py</code> file. This file will contain all our use cases or actions that will be performed, such as CRUD operations.</p>
<h3 id="heading-actionspy">actions.py</h3>
<p><code>app/actions.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any, Dict, Generic, List, Optional, Type, TypeVar, Union

<span class="hljs-keyword">from</span> fastapi.encoders <span class="hljs-keyword">import</span> jsonable_encoder
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> UUID4, BaseModel
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session

<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> schemas
<span class="hljs-keyword">from</span> .db <span class="hljs-keyword">import</span> Base
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post

<span class="hljs-comment"># Define custom types for SQLAlchemy model, and Pydantic schemas</span>
ModelType = TypeVar(<span class="hljs-string">"ModelType"</span>, bound=Base)
CreateSchemaType = TypeVar(<span class="hljs-string">"CreateSchemaType"</span>, bound=BaseModel)
UpdateSchemaType = TypeVar(<span class="hljs-string">"UpdateSchemaType"</span>, bound=BaseModel)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseActions</span>(<span class="hljs-params">Generic[ModelType, CreateSchemaType, UpdateSchemaType]</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, model: Type[ModelType]</span>):</span>
        <span class="hljs-string">"""Base class that can be extend by other action classes.
           Provides basic CRUD and listing operations.

        :param model: The SQLAlchemy model
        :type model: Type[ModelType]
        """</span>
        self.model = model

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_all</span>(<span class="hljs-params">
        self, db: Session, *, skip: int = <span class="hljs-number">0</span>, limit: int = <span class="hljs-number">100</span>
    </span>) -&gt; List[ModelType]:</span>
        <span class="hljs-keyword">return</span> db.query(self.model).offset(skip).limit(limit).all()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span>(<span class="hljs-params">self, db: Session, id: UUID4</span>) -&gt; Optional[ModelType]:</span>
        <span class="hljs-keyword">return</span> db.query(self.model).filter(self.model.id == id).first()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create</span>(<span class="hljs-params">self, db: Session, *, obj_in: CreateSchemaType</span>) -&gt; ModelType:</span>
        obj_in_data = jsonable_encoder(obj_in)
        db_obj = self.model(**obj_in_data)  <span class="hljs-comment"># type: ignore</span>
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        <span class="hljs-keyword">return</span> db_obj

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">
        self,
        db: Session,
        *,
        db_obj: ModelType,
        obj_in: Union[UpdateSchemaType, Dict[str, Any]]
    </span>) -&gt; ModelType:</span>
        obj_data = jsonable_encoder(db_obj)
        <span class="hljs-keyword">if</span> isinstance(obj_in, dict):
            update_data = obj_in
        <span class="hljs-keyword">else</span>:
            update_data = obj_in.dict(exclude_unset=<span class="hljs-literal">True</span>)
        <span class="hljs-keyword">for</span> field <span class="hljs-keyword">in</span> obj_data:
            <span class="hljs-keyword">if</span> field <span class="hljs-keyword">in</span> update_data:
                setattr(db_obj, field, update_data[field])
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        <span class="hljs-keyword">return</span> db_obj

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">remove</span>(<span class="hljs-params">self, db: Session, *, id: UUID4</span>) -&gt; ModelType:</span>
        obj = db.query(self.model).get(id)
        db.delete(obj)
        db.commit()
        <span class="hljs-keyword">return</span> obj


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostActions</span>(<span class="hljs-params">BaseActions[Post, schemas.PostCreate, schemas.PostUpdate]</span>):</span>
    <span class="hljs-string">"""Post actions with basic CRUD operations"""</span>

    <span class="hljs-keyword">pass</span>


post = PostActions(Post)
</code></pre>
<p>Before going back and updating our <code>main.py</code> file, let's review our final directory structure.</p>
<pre><code>.
├── app
│   ├── actions.py
│   ├── config.py
│   ├── __init__.py
│   ├── main.py
│   ├── models.py
│   └── schemas.py
├── poetry.lock
├── pyproject.toml
├── README.md
└── tests
    ├── __init__.py
    └── test_app.py
</code></pre><p>Let's update our <code>main.py</code> file now and connect all the dots. </p>
<h3 id="heading-update-mainpy">Update main.py</h3>
<p><code>app/main.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any, List

<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Depends, FastAPI, HTTPException
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> UUID4
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session
<span class="hljs-keyword">from</span> starlette.status <span class="hljs-keyword">import</span> HTTP_201_CREATED, HTTP_404_NOT_FOUND

<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> actions, models, schemas
<span class="hljs-keyword">from</span> .db <span class="hljs-keyword">import</span> SessionLocal, engine

<span class="hljs-comment"># Create all tables in the database.</span>
<span class="hljs-comment"># Comment this out if you using migrations.</span>
models.Base.metadata.create_all(bind=engine)

app = FastAPI()


<span class="hljs-comment"># Dependency to get DB session.</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_db</span>():</span>
    <span class="hljs-keyword">try</span>:
        db = SessionLocal()
        <span class="hljs-keyword">yield</span> db
    <span class="hljs-keyword">finally</span>:
        db.close()


<span class="hljs-meta">@app.get("/")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world!"</span>}


<span class="hljs-meta">@app.get("/posts", response_model=List[schemas.Post], tags=["posts"])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_posts</span>(<span class="hljs-params">db: Session = Depends(<span class="hljs-params">get_db</span>), skip: int = <span class="hljs-number">0</span>, limit: int = <span class="hljs-number">100</span></span>) -&gt; Any:</span>
    posts = actions.post.get_all(db=db, skip=skip, limit=limit)
    <span class="hljs-keyword">return</span> posts


<span class="hljs-meta">@app.post(</span>
    <span class="hljs-string">"/posts"</span>, response_model=schemas.Post, status_code=HTTP_201_CREATED, tags=[<span class="hljs-string">"posts"</span>]
)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_post</span>(<span class="hljs-params">*, db: Session = Depends(<span class="hljs-params">get_db</span>), post_in: schemas.PostCreate</span>) -&gt; Any:</span>
    post = actions.post.create(db=db, obj_in=post_in)
    <span class="hljs-keyword">return</span> post


<span class="hljs-meta">@app.put(</span>
    <span class="hljs-string">"/posts/{id}"</span>,
    response_model=schemas.Post,
    responses={HTTP_404_NOT_FOUND: {<span class="hljs-string">"model"</span>: schemas.HTTPError}},
    tags=[<span class="hljs-string">"posts"</span>],
)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_post</span>(<span class="hljs-params">
    *, db: Session = Depends(<span class="hljs-params">get_db</span>), id: UUID4, post_in: schemas.PostUpdate,
</span>) -&gt; Any:</span>
    post = actions.post.get(db=db, id=id)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> post:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=HTTP_404_NOT_FOUND, detail=<span class="hljs-string">"Post not found"</span>)
    post = actions.post.update(db=db, db_obj=post, obj_in=post_in)
    <span class="hljs-keyword">return</span> post


<span class="hljs-meta">@app.get(</span>
    <span class="hljs-string">"/posts/{id}"</span>,
    response_model=schemas.Post,
    responses={HTTP_404_NOT_FOUND: {<span class="hljs-string">"model"</span>: schemas.HTTPError}},
    tags=[<span class="hljs-string">"posts"</span>],
)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_post</span>(<span class="hljs-params">*, db: Session = Depends(<span class="hljs-params">get_db</span>), id: UUID4</span>) -&gt; Any:</span>
    post = actions.post.get(db=db, id=id)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> post:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=HTTP_404_NOT_FOUND, detail=<span class="hljs-string">"Post not found"</span>)
    <span class="hljs-keyword">return</span> post


<span class="hljs-meta">@app.delete(</span>
    <span class="hljs-string">"/posts/{id}"</span>,
    response_model=schemas.Post,
    responses={HTTP_404_NOT_FOUND: {<span class="hljs-string">"model"</span>: schemas.HTTPError}},
    tags=[<span class="hljs-string">"posts"</span>],
)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_post</span>(<span class="hljs-params">*, db: Session = Depends(<span class="hljs-params">get_db</span>), id: UUID4</span>) -&gt; Any:</span>
    post = actions.post.get(db=db, id=id)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> post:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=HTTP_404_NOT_FOUND, detail=<span class="hljs-string">"Post not found"</span>)
    post = actions.post.remove(db=db, id=id)
    <span class="hljs-keyword">return</span> post
</code></pre>
<p>Finally, if we run the server again and hit <a target="_blank" href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a> we now have a basic API that can perform CRUD operations on our Post entity. :rocket:</p>
<pre><code class="lang-shell">uvicorn app.main:app
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643282708302/h1EVMXzJV.png" alt="FastAPI - Swagger UI" /></p>
<h2 id="heading-conclusion-bulb">Conclusion :bulb:</h2>
<p>If you have made it this far, well done! :+1: </p>
<p>We created a simple application that can serve as a good starting point for small to medium projects. There are still a number of things we can include in this base project such as migrations or adding Docker to our stack. (<em>Hint: we will cover this in future posts, stay tuned :wink:</em>)</p>
<p>The final code for this post can be found on <a target="_blank" href="https://github.com/alexvanzyl/fastapi-simple-app-example/tree/part-1">GitHub</a>.</p>
<p>If you enjoyed reading this article and would like to stay tuned for more, or just want to connect, follow me on twitter <a target="_blank" href="https://twitter.com/alexvanzyl">@alexvanzyl</a>.</p>
]]></content:encoded></item></channel></rss>