<?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[Josh's Blog; Data.Code.Cloud]]></title><description><![CDATA[Data.Code.Cloud]]></description><link>https://itsjoshcampos.codes</link><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 15:17:22 GMT</lastBuildDate><atom:link href="https://itsjoshcampos.codes/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[.NET Web API - API Key Authorization]]></title><description><![CDATA[Simple API Lockdown
Introduction
The previous article reviewed how to set and read environment variables. We'll use that knowledge and set up an API Key as an environment variable for authorization in a .NET Web API Project. Keys are a variable you w...]]></description><link>https://itsjoshcampos.codes/net-web-api-api-key-authorization</link><guid isPermaLink="true">https://itsjoshcampos.codes/net-web-api-api-key-authorization</guid><category><![CDATA[dotnet]]></category><category><![CDATA[macOS]]></category><category><![CDATA[APIs]]></category><category><![CDATA[visual studio]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Sat, 25 Mar 2023 21:16:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679778002361/288d26c1-5ad4-4fc8-9d56-a2b46ad5ca7b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Simple API Lockdown</p>
<h1 id="heading-introduction">Introduction</h1>
<p>The previous article reviewed how to set and read environment variables. We'll use that knowledge and set up an API Key as an environment variable for authorization in a .NET Web API Project. Keys are a variable you would not want to check into your code repo, so environment variables are a perfect place for them. We don't want to store keys in our <code>appsettings.json</code> file, you can also store them in a 3rd party key vault or whatever you prefer outside of your project.</p>
<p>An API Key can serve two main purposes, a method of identifying the caller, client, or requestor of the API Service and also controlling access to the API Service. It is a simple method of security to protect API resources.</p>
<blockquote>
<p>API keys are a common approach to auth when using microservices, having internal services communicate with each other.</p>
</blockquote>
<p>API keys can be managed in a couple of different ways. For example, one approach is to create client-specific keys. The key can be used to validate the requesting service in order to provide specific access to one service versus another such as rate limiting, read vs write access, performance priority, etc. You need to manage to create multiple keys and assign them to the correct party. That is outside the scope of this article. Another approach is using a static key that can be provided to all API client services for authorization. "One key to rule them all..." this is the use case we'll walkthrough in this article.</p>
<p>Code for this example can be found here - <a target="_blank" href="https://github.com/ItsJoshCampos/dotnet-api-series/tree/main/article-3-api-key/article-3-api-key">API Key Project Repo</a>. Continuing along with the other articles in this series I'll be building in Visual Studio for Mac using .NET 6.</p>
<h1 id="heading-setup-an-api-key-environment-variable">Setup an API Key Environment Variable</h1>
<p>For this walkthrough, we will store the API Key in <code>Visual Studio for Mac's</code> Environment Variable properties.</p>
<p>This project will be a new build from scratch, below are the settings I'm using.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679766535954/293c78f8-7a3d-432c-bfa6-12d030f88385.png" alt class="image--center mx-auto" /></p>
<p>After your new project is scaffolded out, go to the Project's Properties window by either right-clicking the project folder in the solution explorer window or in the menu bar click on Project and select the Properties in the drop-down.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679778349477/a7185781-f2c3-44d0-a7c5-2fd4b6f60add.png" alt class="image--center mx-auto" /></p>
<p>In the Properties window go to the <code>Run</code> &gt; <code>Configurations</code> &gt; <code>Default</code> option and add a new Environment Variable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679770512524/9da7e8f6-e1f0-449e-93ea-0d518a7ec074.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>⚠️ Environment variables create in Visual Studio are copied to your <code>launchSettings.json</code> file in the projects Properties folder. This file is not in your <code>.gitignore</code> file by default.</p>
<p>Your ENV variables should not be in your code, this is just a development example.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679770663886/c5c744c2-ffb9-4a3c-bab1-a45d8d136527.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-middleware">Middleware</h1>
<p>We need to add a new directory named <code>Authentication</code> for our changes. We will be creating a middleware that will be injected into the application request pipeline.</p>
<blockquote>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0">.NET Middleware</a> is the pipeline that handles requests and responses in the application. Request Delegates are the individual stops along the pipeline. We use <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0">Dependency Injection</a> to add the <code>RequestDelete</code> parameter to our custom middleware class <code>AuthApiKeyMiddleware</code>. <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-6.0">Writing custom middleware documentation is available on the Microsoft Docs site.</a></p>
</blockquote>
<p>The <code>InvokeAsync</code> method will add the <code>HttpContext</code> in order to access all the details of the request such as headers, user context, query strings, etc.</p>
<p>The <code>InvokeAsync</code> method will perform two main tasks:</p>
<ol>
<li><p>Check if the request includes the <code>x-api-key</code> header.</p>
</li>
<li><p>If the request does include the <code>x-api-key</code> header, make sure it is valid.</p>
</li>
</ol>
<p>In either case, if the <code>x-api-key</code> header is missing or the value is not valid, the middleware will return an <code>UNAUTHORIZED: 401</code> response <strong>before even hitting</strong> our API endpoint.</p>
<p>In the event that the <code>x-api-key</code> is valid, then the request <code>context</code> is <strong>passed onto the next delegate</strong> in the pipeline.</p>
<blockquote>
<p>⚠️ This auth middleware will be applied to all endpoints in the project. If you only want to restrict specific endpoints an Auth Attribute will need to be created instead. We'll cover than later.</p>
</blockquote>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">article_3_api_key.Authentication</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AuthApiKeyMiddleware</span>
    {
        <span class="hljs-comment">// Request Delete that is used to manage each HTTP request.</span>
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> RequestDelegate _next;
        <span class="hljs-comment">// API Key Header Name</span>
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> ApiKeyHeader = <span class="hljs-string">"x-api-key"</span>;

        <span class="hljs-comment">// Inject Request Delegate into API Key Middleware</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AuthApiKeyMiddleware</span>(<span class="hljs-params">RequestDelegate next</span>)</span>
        {
            _next = next;
        }

        <span class="hljs-comment">// Pass the Request's HTTP Context</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InvokeAsync</span>(<span class="hljs-params">HttpContext context</span>)</span>
        {
            <span class="hljs-comment">// Look for the "x-api-key" Header in the request</span>
            <span class="hljs-keyword">if</span>(!context.Request.Headers.TryGetValue(ApiKeyHeader, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> extractedApiKey))
            {
                <span class="hljs-comment">// If not found, throw a 401 status</span>
                <span class="hljs-comment">// 401 = Invalid or Missing Credentials</span>
                <span class="hljs-comment">// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401</span>
                context.Response.StatusCode = <span class="hljs-number">401</span>;
                <span class="hljs-keyword">await</span> context.Response.WriteAsync(<span class="hljs-string">"UNAUTHORIZED: API Key Missing"</span>);
                <span class="hljs-keyword">return</span>;
            }

            <span class="hljs-comment">// Get the real key from our ENV Variable</span>
            <span class="hljs-keyword">var</span> apiVal = Environment.GetEnvironmentVariable(ApiKeyHeader);

            <span class="hljs-comment">// API Key found in Header</span>
            <span class="hljs-comment">// Validate the Key</span>
            <span class="hljs-keyword">if</span>(!apiVal.Equals(extractedApiKey))
            {
                <span class="hljs-comment">// If key is not valid, throw a 401 status</span>
                <span class="hljs-comment">// 401 = Invalid or Missing Credentials</span>
                <span class="hljs-comment">// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401</span>
                context.Response.StatusCode = <span class="hljs-number">401</span>;
                <span class="hljs-keyword">await</span> context.Response.WriteAsync(<span class="hljs-string">"UNAUTHORIZED: Invalid API Key"</span>);
            }

            <span class="hljs-comment">// Valid API Key was provided</span>
            <span class="hljs-comment">// Pass request to the next delegate in the pipeline</span>
            <span class="hljs-keyword">await</span> _next(context);
        }
    }
}
</code></pre>
<p>To use this middleware in the application pipeline, we need to add a single line of code in the <code>Program.cs</code> file. Below is a section of the file showing the use of the new middleware.</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// Program.cs FILE</span>
...

<span class="hljs-comment">// NEW LINE: Add new API KEY Middleware to pipeline</span>
app.UseMiddleware&lt;AuthApiKeyMiddleware&gt;();

app.UseAuthorization();

app.MapControllers();

app.Run();
</code></pre>
<p>Start the project to test the changes. Click the play button in the top menu of Visual Studio.</p>
<h1 id="heading-try-it-out">Try it out</h1>
<p>I'm going to hit the API using CURL statements... 3 different attempts to see the application's response for the following cases: Missing API Key, Invalid API Key, and Valid API Key.</p>
<h2 id="heading-missing-api-key">Missing API Key</h2>
<pre><code class="lang-bash">curl -X GET http://localhost:5276/WeatherForecast
</code></pre>
<p>You should see the following unauthorized response:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679773512950/b71acfde-266b-4e92-8fc4-94881a083663.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-invalid-api-key">Invalid API Key</h2>
<pre><code class="lang-bash">curl -X GET http://localhost:5276/WeatherForecast  -H <span class="hljs-string">'x-api-key: WrongKey'</span>
</code></pre>
<p>You should see the following unauthorized response:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679773595841/bd3e8879-dff3-45ad-a5b4-0dca9f7faf4d.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-valid-api-key">Valid API Key</h2>
<pre><code class="lang-bash">curl -X GET http://localhost:5276/WeatherForecast  -H <span class="hljs-string">'x-api-key: ThisIsMySecureKey!'</span>
</code></pre>
<p>You should see the weather response below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679773343929/9651b4e1-a913-413e-97c8-47af3e2f70e4.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-swagger-support">Swagger Support</h1>
<p>If you try hitting the weather endpoint in Swagger you will receive a 401 response. Swagger is not sending the API key along with the request so each request is rejected. Currently, there is no place to add the API Key to your requests. Let's fix that.</p>
<p>In the <code>Programs.cs</code> file update the <code>builder.Services.AddSwaggerGen</code> extension. The code is shown below.</p>
<p>We are specifying the "Security Definition" for an input to build the header named <code>x-api-key</code>.</p>
<p>We then add a Security Requirement based on a scheme that will use the API key header.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Program.cs</span>
...

builder.Services.AddSwaggerGen(x =&gt;
{
    x.AddSecurityDefinition(<span class="hljs-string">"Weather API Key"</span>, <span class="hljs-keyword">new</span> OpenApiSecurityScheme
    {
        <span class="hljs-comment">// Header Name</span>
        Name = <span class="hljs-string">"x-api-key"</span>,
        Type = SecuritySchemeType.ApiKey,
        Scheme = <span class="hljs-string">"ApiKeyScheme"</span>,
        In = ParameterLocation.Header,
        Description = <span class="hljs-string">"API Key Header Requirement"</span>
    });

    x.AddSecurityRequirement(<span class="hljs-keyword">new</span> OpenApiSecurityRequirement
    {
        {
            <span class="hljs-keyword">new</span> OpenApiSecurityScheme
            {
                Reference = <span class="hljs-keyword">new</span> OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = <span class="hljs-string">"Weather API Key"</span>
                },
                <span class="hljs-comment">// Scheme using Header</span>
                In = ParameterLocation.Header
            },
            <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;()
        }
    });
});

...
</code></pre>
<p>Let's start up the project and use Swagger.</p>
<p>You will now see a green Authorize button that was not available before.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679774972503/88d3d539-3122-49a4-80ba-42261e6d8977.png" alt class="image--center mx-auto" /></p>
<p>Click on Authorize and enter your API Key and click Authorize to save your input and Close to close the prompt.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679775348784/c003879d-055d-406c-a4ff-fa4825c9baae.png" alt class="image--center mx-auto" /></p>
<p>You can now try to execute the endpoint and you will see the weather data returned.</p>
<h1 id="heading-attributes">Attributes</h1>
<p>For scenarios where the flexibility of securing specific endpoints is required <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/creating-custom-attributes">.NET Custom Attributes</a> can be used instead of Middleware. Again, the middleware implemented before was locking down all endpoints for all controllers.</p>
<p>We first need to comment out the line in <code>Program.cs</code> to use the new API Key middleware, we don't want to use that delegate for every API request for this example. We'll use attributes at a Controller (Class) and Endpoint (Method) level of execution to validate the API key.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Program.cs</span>

...
<span class="hljs-comment">//app.UseMiddleware&lt;AuthApiKeyMiddleware&gt;();</span>
...
</code></pre>
<p>Next, create a new Attribute class, <code>AuthApiKeyAttribute</code> in the <code>Authentication</code> folder.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc.Filters;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">article_3_api_key.Authentication</span>
{
    [<span class="hljs-meta">AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AuthApiKeyAttribute</span> : <span class="hljs-title">Attribute</span>, <span class="hljs-title">IAsyncActionFilter</span>
    {
        <span class="hljs-comment">// API Key Header and ENV Name</span>
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> ApiKeyHeader = <span class="hljs-string">"x-api-key"</span>;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">OnActionExecutionAsync</span>(<span class="hljs-params">ActionExecutingContext context, ActionExecutionDelegate next</span>)</span>
        {
            <span class="hljs-comment">// Look for the "x-api-key" Header in the request</span>
            <span class="hljs-keyword">if</span> (!context.HttpContext.Request.Headers.TryGetValue(ApiKeyHeader, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> apiKeyVal))
            {
                <span class="hljs-comment">// If not found, throw a 401 status</span>
                <span class="hljs-comment">// 401 = Invalid or Missing Credentials</span>
                <span class="hljs-comment">// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401</span>
                context.HttpContext.Response.StatusCode = <span class="hljs-number">401</span>;
                <span class="hljs-keyword">await</span> context.HttpContext.Response.WriteAsync(<span class="hljs-string">"UNAUTHORIZED: API Key Missing"</span>);
            }

            <span class="hljs-comment">// Get the real key from our ENV Variable</span>
            <span class="hljs-keyword">var</span> apiVal = Environment.GetEnvironmentVariable(ApiKeyHeader);
            <span class="hljs-keyword">if</span> (!apiVal.Equals(apiKeyVal))
            {
                <span class="hljs-comment">// If not found, throw a 401 status</span>
                <span class="hljs-comment">// 401 = Invalid or Missing Credentials</span>
                <span class="hljs-comment">// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401</span>
                context.HttpContext.Response.StatusCode = <span class="hljs-number">401</span>;
                <span class="hljs-keyword">await</span> context.HttpContext.Response.WriteAsync(<span class="hljs-string">"UNAUTHORIZED: Invalid API Key"</span>);
            }

            <span class="hljs-keyword">await</span> next();
        }
    }
}
</code></pre>
<p>The new class will specify a base class of <code>Attribute</code> and <code>IAsyncActionFilter</code>. The <code>AttributeUsage</code> decorator is also an Attribute that indicates that this Attribute class will be used on classes (Controllers) and methods (endpoints/ actions).</p>
<p>Outside of the class decorations, the <code>OnActionExecutionAsync</code> method is identical to the middleware example. We are looking to ensure the API key header is included and if it is, make sure it is valid.</p>
<p>To test that the Attribute only works on specific controller endpoints and not all of them, we are going to create a new endpoint.</p>
<p>In the <code>WeatherForecastController</code>, change the attributes on the existing endpoint to use the <code>AuthAPIKey</code> attribute. A <code>using</code> statement will be required to point to the new class file. Another change is that "Secure" is added to the route.</p>
<p>The second endpoint is a copy of the first endpoint. All but the <code>AuthAPIKey</code> Attribute is not specified. The route of this endpoint is "Open".</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// WeatherForecastController.cs</span>
<span class="hljs-keyword">using</span> article_3_api_key.Authentication;
...    

[AuthApiKey]
[HttpGet]
[Route(<span class="hljs-string">"Secure"</span>)]
<span class="hljs-function"><span class="hljs-keyword">public</span> IEnumerable&lt;WeatherForecast&gt; <span class="hljs-title">Get</span><span class="hljs-params">()</span>

...

[HttpGet]
[<span class="hljs-title">Route</span><span class="hljs-params">(<span class="hljs-string">"Open"</span>)</span>]
<span class="hljs-keyword">public</span> IEnumerable&lt;WeatherForecast&gt; <span class="hljs-title">GetTest</span><span class="hljs-params">()</span>

...</span>
</code></pre>
<p>Start up the project and let's explore the changes in Swagger.</p>
<h3 id="heading-open-endpoint">Open Endpoint</h3>
<p>Without entering your API Key, execute the <code>/WeatherForecast/Open</code> endpoint and you will be able to see the weather data returned with no restrictions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679777486479/b5eed3a3-bf56-4499-a15f-471ee3ba2c59.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-secure-endpoint-via-attribute">Secure Endpoint via Attribute</h3>
<p>Executing the Secure endpoint will result in a 401 Unauthorized Response without the API key. The Controller will require the Auth API Key Attribute to be run before hitting the endpoint.</p>
<p>Enter your API key and try again, you will see the lock icon activated in Swagger and the data is now returned from a secure route.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679777744339/80c3d553-c18d-4768-9bc4-e56b2ef023d4.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-end">End</h1>
<p>You now have a working API Key Middleware and Attribute option for a .NET 6 Web API Project.</p>
]]></content:encoded></item><item><title><![CDATA[.NET Web API - Environment Variables]]></title><description><![CDATA[Introduction
The first article in this series was the default WeatherForecast model that is created by default in a .NET Web API project. We'll build off Article 1 to start reading in values from environment variables and appsettings.json files.
The ...]]></description><link>https://itsjoshcampos.codes/net-web-api-environment-variables</link><guid isPermaLink="true">https://itsjoshcampos.codes/net-web-api-environment-variables</guid><category><![CDATA[dotnet]]></category><category><![CDATA[macOS]]></category><category><![CDATA[APIs]]></category><category><![CDATA[visual studio]]></category><category><![CDATA[Environment variables]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Fri, 03 Mar 2023 04:57:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1677731762862/c5c5ee93-0617-4719-b9fa-6a328a5438f8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>The first article in this series was the default <code>WeatherForecast</code> model that is created by default in a .NET Web API project. We'll build off Article 1 to start reading in values from environment variables and <code>appsettings.json</code> files.</p>
<p>The code for this article can be found in the following <a target="_blank" href="https://github.com/ItsJoshCampos/dotnet-api-series/tree/main/article-2-environment-variables">GitHub Repo - Article 2</a>. The repository will contain a dedicated directory for each article in the series.</p>
<h2 id="heading-environment-variables">Environment Variables</h2>
<p>Environment variables are used for specifying external configurations or settings such as credentials, secrets, service configs, and any important stuff we need to remember. Not just are they important but they can change from time to time, so they need to be in a location for quick access to change and be available for any resource or application to pick up these changes.</p>
<p>First, let's review how we set environment variables on macOS or similar *nix environments. Read my short article on <a target="_blank" href="https://itsjoshcampos.codes/4-options-to-create-environment-variables-on-nix-machines">creating Environment Variables</a> as a refresher.</p>
<p>Now let's review how to read those environment variables in a .NET Web API project.</p>
<h2 id="heading-read-runtime-environment-in-net">Read Runtime Environment in .NET</h2>
<p>One of the primary reasons to read an environment variable is to identify the correct environment your application/ service is running in. Visual Studio, on both PC and Mac, has a built-in Environment Variable Configuration feature built into the IDE.</p>
<p>We can set any value we'd like to use in our application by using the .NET Web API project's properties. Right-click the main project in the Visual Studio Solution Explorer and select Properties. In the Properties window, Go to the Run &gt; Configurations &gt; Default section. You'll see the Environment Variables table.</p>
<p>The example project we built in Article-1 is setting the <code>ASPNETCORE_ENVIRONMENT</code> variable to the value <code>Development</code>. The <code>ASPNETCORE_ENVIRONMENT</code> is a reserved word for .NET to automatically assign it to the application's Hosting Environment. <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-6.0#environments">You can read further detail about .NET Runtime Environments here.</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677794754454/ba709114-94b7-493f-8170-07008a979849.png" alt class="image--center mx-auto" /></p>
<p>Back to the code: the <code>Program.cs</code> file is using the <code>ASPNETCORE_ENVIRONMENT</code> variable to determine if the environment is set to Development. The <code>if</code> statement on line 38 will determine if the Swagger (OpenAPI) Documentation should be displayed. In the screenshot below I'm running the project and set a breakpoint on line 19. I'm hovering over the <code>app.Environment</code> variable. As you can see .NET read in the value and the <code>EnvironmentName</code> is set to Development.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677818700251/4c13e805-102d-4164-896d-c23e2bdd12b2.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-read-environment-variables-in-net">Read Environment Variables in .NET</h2>
<p>Now let's review custom environment variables, I'm going to create a new env variable <code>VS_ENV</code> in the Visual Studio properties window and one env variable <code>MACOS_ENV</code> in macOS (<code>.zshrc</code> file). If you are using bash, it would be created in your <code>.bashrc</code> file. Below are screenshots of the newly created env variables.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677799149400/34f2fe7f-5534-4739-aff9-1a0063e91ba9.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>⚠️ WARNING! ⚠️</p>
<p>Visual Studio won't read in your MACOS (export env variables) by default. In order to have Visual Studio read in OS env variables you need to start Visual Studio from your terminal. You can't double click the icon. Shut down your current instance of Visual Studio and start the application from your terminal.</p>
<p>To start Visual Studio from your terminal use the following command in your terminal:</p>
<p><code>open -n "/Applications/Visual Studio.app"</code></p>
</blockquote>
<p>Now back to work, add the following code to your <code>Program.cs</code> file to read our new env variables values.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Taken from Program.cs</span>
...

<span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-keyword">var</span> vs_env = Environment.GetEnvironmentVariable(<span class="hljs-string">"VS_ENV"</span>));
<span class="hljs-keyword">var</span> macos_env = Environment.GetEnvironmentVariable(<span class="hljs-string">"MAC_ENV"</span>));

<span class="hljs-keyword">var</span> dict = Environment.GetEnvironmentVariables();
<span class="hljs-comment">//Add Breakpoint on next line</span>
<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> d <span class="hljs-keyword">in</span> dict)
{
    Console.WriteLine(d);
}

...
</code></pre>
<p>Add a breakpoint stopping on the <code>foreach</code> statement and run the project. On start-up, hover over the <code>dict</code> variable and you'll see all environment variables that are read in from Visual Studio. You can hover over the <code>vs_env</code> and <code>macos_env</code> variables and see the values we created. Cool, we can read env variables now.</p>
<p>Starting Visual Studio on your Mac from the terminal is not something you want to do each time you start the app. For that reason, I stick to using environment variables from either the project's properties windows we used earlier or I use the project's <code>appsettings.json</code> files. These files can be set to use a runtime environment automatically. Based on the <code>ASPNETCORE_ENVIRONMENT</code> variable, .NET will load the matching <code>appsettings.json</code> file. Let's test it.</p>
<h2 id="heading-read-appsettingsjson-variables-in-net">Read <code>appsettings.json</code> Variables in .NET</h2>
<p>In .NET 6, the <code>WebApplicationBuilder</code> class exposes the <code>Configuration</code> properties to the application. The <code>Configuration</code> class makes it easy to read values from the <code>appsettings.{RuntimeEnvironment}.json</code> files.</p>
<p>Let's create a new file at the application root of the project named: <code>appsettings.staging.json</code>. Visual Studio will automatically nest app settings environment files under the main <code>appsettings.json</code> file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677800512860/4d7e22a8-2265-4e74-b225-af3fb914dcc8.png" alt class="image--center mx-auto" /></p>
<p>The app setting files should have the following content:</p>
<pre><code class="lang-json"><span class="hljs-comment">// appsettings.Development.json</span>
{
  <span class="hljs-attr">"Logging"</span>: {
    <span class="hljs-attr">"LogLevel"</span>: {
      <span class="hljs-attr">"Default"</span>: <span class="hljs-string">"Information"</span>,
      <span class="hljs-attr">"Microsoft.AspNetCore"</span>: <span class="hljs-string">"Warning"</span>
    }
  },
  <span class="hljs-attr">"MyENV"</span>: <span class="hljs-string">"ImFromDevelopment"</span>
}



<span class="hljs-comment">// appsettings.Staging.json</span>
{
  <span class="hljs-attr">"Logging"</span>: {
    <span class="hljs-attr">"LogLevel"</span>: {
      <span class="hljs-attr">"Default"</span>: <span class="hljs-string">"Information"</span>,
      <span class="hljs-attr">"Microsoft.AspNetCore"</span>: <span class="hljs-string">"Warning"</span>
    }
  },
  <span class="hljs-attr">"MyENV"</span>: <span class="hljs-string">"ImFromStaging"</span>
}
</code></pre>
<p>In the <code>Program.cs</code> file we'll add two lines of code to read the configuration value <code>MyENV</code>. Below is what your <code>Program.cs</code> files should look like. Values from the <code>appsettings.json</code> files can be read in multiple ways. The first new line of code is using square bracket notation and the second new line of code is using the <code>GetValue&lt;Type&gt;</code> method to read the same value.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677800741662/4fc93482-6969-47f8-9464-d13dfb3f2f9e.png" alt class="image--center mx-auto" /></p>
<p>We will run this project two times. One time with the Visual Studio environment variable <code>ASPNETCORE_ENVIRONMENT=Development</code> and another run with the environment variable <code>ASPNETCORE_ENVIRONMENT=Staging</code>. The screenshot below shows the values read from each runtime environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677801430261/7d5bc74b-9b49-4f45-889f-a626456eec6d.png" alt class="image--center mx-auto" /></p>
<p>There you have it, Visual Studio will load the correct <code>appsettings.json</code> file based on your <code>ASPNETCORE_ENVIRONMENT</code> variable automatically. This is great for local development and you can write to the app setting files via CI/CD builds and deploy builds with the correct variables based on the runtime environment.</p>
<h2 id="heading-strongly-typed-settings-options-pattern">Strongly Typed Settings - Options Pattern</h2>
<p>Well now use the Options Pattern to provide the variable/config/setting values from the <code>appsettings.json</code> file into a class structure that can be utilized throughout your application.</p>
<pre><code class="lang-json"><span class="hljs-comment">// Updated appsettings.json</span>

{
  <span class="hljs-attr">"Logging"</span>: {
    <span class="hljs-attr">"LogLevel"</span>: {
      <span class="hljs-attr">"Default"</span>: <span class="hljs-string">"Information"</span>,
      <span class="hljs-attr">"Microsoft.AspNetCore"</span>: <span class="hljs-string">"Warning"</span>
    }
  },
  <span class="hljs-attr">"AllowedHosts"</span>: <span class="hljs-string">"*"</span>,
  <span class="hljs-attr">"SampleClass"</span>: {
    <span class="hljs-attr">"StringValue"</span>: <span class="hljs-string">"String Sample"</span>,
    <span class="hljs-attr">"BooleanValue"</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p>We'll create the <code>SampleClass.cs</code> as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// New file, SampleClass.cs</span>

<span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">article_2_environment_variables</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">SampleClass</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> StringValue { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> BooleanValue { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>Next, add a single line in the <code>Program.cs</code> file to bind the values from the JSON file to the class object. That's it.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Program.cs File</span>

...

builder.Services.Configure&lt;SampleClass&gt;(builder.Configuration.GetSection(<span class="hljs-keyword">nameof</span>(SampleClass)));

...
</code></pre>
<p>You can now use <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0#recommendations">Dependency Injection (DI)</a> to use these values in your <code>WeatherForecastController</code>. Setup your class object in the controller's constructor as below. DI is a recommended approach instead of global/ static variables.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// WeatherForecastController.cs</span>

...

<span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> SampleClass _sampleClass;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WeatherForecastController</span>(<span class="hljs-params">ILogger&lt;WeatherForecastController&gt; logger, IOptions&lt;SampleClass&gt; sampleClassOptions</span>)</span>
{
    _logger = logger;
    _sampleClass = sampleClassOptions.Value;
}

 [<span class="hljs-meta">HttpGet</span>]
 [<span class="hljs-meta">Route(<span class="hljs-meta-string">"SampleClass"</span>)</span>]
 <span class="hljs-function"><span class="hljs-keyword">public</span> SampleClass <span class="hljs-title">GetSampleClass</span>(<span class="hljs-params"></span>)</span>
 {
    <span class="hljs-keyword">return</span> _sampleClass;
 }

...
</code></pre>
<p>When running the project (set <code>ASPNETCORE_ENVIRONMENT=Development</code> you can now hit this new <code>/SampleClass</code> endpoint and see the values returned from your API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677818170281/13c49fdd-9c59-4517-8f85-943c4f692480.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-end">End</h1>
<p>The official .NET docs are great. Read further details <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/?view=aspnetcore-6.0">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[.NET Web API - Getting Started]]></title><description><![CDATA[Introduction
This article is the start of a series on how to build an API using the .NET Framework. I'm sticking with .NET 6, it is the latest Long-Term-Support (LTS) Version.

I'll be using Visual Studio for Mac as my IDE. Before you start yelling, ...]]></description><link>https://itsjoshcampos.codes/net-web-api-getting-started</link><guid isPermaLink="true">https://itsjoshcampos.codes/net-web-api-getting-started</guid><category><![CDATA[dotnet]]></category><category><![CDATA[macOS]]></category><category><![CDATA[api]]></category><category><![CDATA[visual studio]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Wed, 01 Mar 2023 14:58:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1677642684653/4e69a286-1e96-4295-90a3-ddce47e7ef81.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>This article is the start of a series on how to build an API using the <a target="_blank" href="https://dotnet.microsoft.com/en-us/download">.NET</a> Framework. I'm sticking with .NET 6, it is the latest Long-Term-Support (LTS) Version.</p>
<blockquote>
<p>I'll be using Visual Studio for Mac as my IDE. Before you start yelling, I love VS Code too, I use it for everything other than .NET Web API development. The built-in dev web server, class/ controller file generation, project generation, etc. is just a simpler and faster experience than hand typing everything via terminal. Don't hate. Moving on.</p>
</blockquote>
<h1 id="heading-environment">Environment</h1>
<p>This walkthrough will be built on macOS. However, no reason you can't follow along on Windows as well.</p>
<p>The code for this article can be found in the following <a target="_blank" href="https://github.com/ItsJoshCampos/dotnet-api-series">GitHub Repo</a>. The repository will contain a dedicated directory for each article in the series.</p>
<h1 id="heading-tools">Tools</h1>
<ul>
<li><p><a target="_blank" href="https://visualstudio.microsoft.com/vs/mac/">Visual Studio for Mac</a></p>
<ul>
<li>As of March 2023, installing Visual Studio for Mac will also install the .NET 7 SDK, which is not LTS. So we'll manually download and install the [.NET 6 SDK](<a target="_blank" href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0">https://dotnet.microsoft.com/en-us/download/dotnet/6.0</a>) to use on this walkthrough.</li>
</ul>
</li>
<li><p>macOS</p>
</li>
</ul>
<h1 id="heading-creating-project">Creating Project</h1>
<p>Let's get started creating our project.</p>
<p>Start up Visual Studio for Mac and select the <code>New</code> project button.</p>
<p>We'll choose the "Web and Console" &gt; "App" category and select the API (C#) template and click Continue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677647065240/67a61696-99f5-4252-bc1c-36d09704f936.png" alt="Starting dotnet Project" class="image--center mx-auto" /></p>
<p>The next step of the project wizard will prompt you to select the Target framework. Again, we're sticking with .NET 6.0. For the Advanced settings, we'll select the following options:</p>
<ul>
<li><p>No need for HTTPS for this walkthrough.</p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0">Enable the use of controllers</a>. We'll explore the Minimal API design approach in later articles.</p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-6.0">Enable OpenAPI support</a>. We'll use the UI-based OpenAPI spec AKA Swagger documentation tool.</p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements">Enable top-level Statements</a>. I also build in Python from time to time, so the fewer brackets the better. This is a recent feature available in C#10.</p>
</li>
</ul>
<p>Click continue to move on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677648152144/91029519-4df0-46e2-b1cc-74d8af00b0ad.png" alt="Starting dotnet Project Settings" class="image--center mx-auto" /></p>
<p>Choose your local directory for the project and leave the project in a subfolder to keep it organized.</p>
<p>Click Create.</p>
<h1 id="heading-running-the-project">Running the Project</h1>
<p>The default project will include an example <code>WeatherForecast.cs</code> class and the <code>WeatherForecastController.cs</code> controller file.</p>
<p>You should see a window with the directory structure looking just like the image below. Click the Play icon in the toolbar to run the default project.</p>
<p>Starting up the project will do the following:</p>
<ul>
<li><p>Load all dependencies listed in the Dependencies folder, specifically Nuget, the .NET package manager.</p>
</li>
<li><p>Once all packages are downloaded Visual Studio will build the project producing compiled executable files to run.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677648664013/109f1182-f933-43e7-acb0-e0238561280f.png" alt="Visual Studio Project Start" class="image--center mx-auto" /></p>
<p>Visual Studio will start up your default browser using the <code>applicationUrl</code> specified in your <code>launchSettings.json</code> file. The JSON code below is a sample of my project. The application started up automatically using the URL: <code>http://localhost:5084</code> and added the launch path: <code>swagger</code> to automatically load the OpenAPI (Swagger) web page.</p>
<pre><code class="lang-json">...
<span class="hljs-comment">// Sample from Properties &gt; launchSettings.json </span>

...

  <span class="hljs-string">"profiles"</span>: {
    <span class="hljs-attr">"article_1_getting_started"</span>: {
      <span class="hljs-attr">"commandName"</span>: <span class="hljs-string">"Project"</span>,
      <span class="hljs-attr">"launchBrowser"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"launchUrl"</span>: <span class="hljs-string">"swagger"</span>,
      <span class="hljs-attr">"applicationUrl"</span>: <span class="hljs-string">"http://localhost:5084"</span>,
      <span class="hljs-attr">"environmentVariables"</span>: {
        <span class="hljs-attr">"ASPNETCORE_ENVIRONMENT"</span>: <span class="hljs-string">"Development"</span>
      }
    },

...
</code></pre>
<p>The Swagger UI tool is building an XML file based on the OpenAPI specifications interpreted in your project. You can run the example WeatherForecast endpoint via the UI or copy the CURL statement if you want to hit the endpoint from your terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677649229075/8e50847c-858e-45e9-bb57-18f32d523016.png" alt="Visual Studio OpenAPI" class="image--center mx-auto" /></p>
<p>That's all for now, getting started with a .NET 6 Web API project. Click the square icon (where the play button was) in the Visual Studio toolbar to quit the project.</p>
<p>Future articles in this series will cover further configurations regarding security, database connections, and more.</p>
]]></content:encoded></item><item><title><![CDATA[Fast API - Running in Docker]]></title><description><![CDATA[Introduction
This article will setup a basic Hello World FastAPI project running in Docker. The purpose is to start off small before starting on a full real world project.  
What and Why
Docker is a containerization platform.  Docker combines applica...]]></description><link>https://itsjoshcampos.codes/fast-api-running-in-docker</link><guid isPermaLink="true">https://itsjoshcampos.codes/fast-api-running-in-docker</guid><category><![CDATA[FastAPI]]></category><category><![CDATA[Python]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Sat, 13 Aug 2022 03:34:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660361580118/gVbrxW_qi.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>This article will setup a basic <code>Hello World</code> FastAPI project running in Docker. The purpose is to start off small before starting on a full real world project.  </p>
<h1 id="heading-what-and-why">What and Why</h1>
<p>Docker is a containerization platform.  Docker combines application source code and dependencies so you can run your code in any environment.  The container engine runs directly on your Operating System, so containers can be scaled directly on your host... assuming it can handle the compute and storage load. </p>
<p>There are so many resources for Docker, including Docker's home site.  No quick and easy way to learn Docker.  If you need a further deep dive take a look at <a target="_blank" href="https://docs.docker.com/get-started/">Docker's Getting Started</a> pages.</p>
<p>You can also follow our <a target="_blank" href="https://itsjoshcampos.codes/run-sql-server-on-macos-docker">SQL Server on macOS using Docker</a> article to get Docker Desktop installed if you don't have it already.</p>
<h1 id="heading-code-repo">Code Repo</h1>
<p>Code for this article can be found in the following <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-5-docker">GitHub Repo - Article 5</a>. </p>
<h2 id="heading-setup-fastapi">Setup FastAPI</h2>
<p>The API code is a simple example for now.  Our focus is on the detail of the DockerFile and configuration.</p>
<p>Our FastAPI project will only have one route/ endpoint to return <code>Hello World</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>
<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-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> { <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world"</span> }
</code></pre>
<p>Our python <code>requirements.txt</code> file only includes the basics, FastAPI and our web server, Uvicorn:</p>
<pre><code class="lang-txt">fastapi&gt;=0.68.0,&lt;0.69.0
uvicorn&gt;=0.15.0,&lt;0.16.0
</code></pre>
<h2 id="heading-setup-docker">Setup Docker</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># Using Python 3.9 </span>
FROM python:3.9

<span class="hljs-comment"># Setup working directory</span>
RUN mkdir code
WORKDIR /code

<span class="hljs-comment"># Copy requirements file to our working directory</span>
COPY ./requirements.txt /code/requirements.txt

<span class="hljs-comment"># Install packages - Use cache dependencies </span>
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

<span class="hljs-comment"># Copy our code over to our working directory</span>
COPY ./ /code/app

<span class="hljs-comment"># Run our project exposed on port 80</span>
CMD [<span class="hljs-string">"uvicorn"</span>, <span class="hljs-string">"app.app:app"</span>, <span class="hljs-string">"--host"</span>, <span class="hljs-string">"0.0.0.0"</span>, <span class="hljs-string">"--port"</span>, <span class="hljs-string">"80"</span>]
</code></pre>
<p>Now let's start decoding the DockerFile.  </p>
<h3 id="heading-from">FROM</h3>
<p>This statement is your starting command which initializes a new build and sets the base image that your environment will be working with. A Dockerfile has to start with a FROM statement. </p>
<blockquote>
<p>Containers work in a layered environment, so picture this as your most fundamental dev layer of your container.  </p>
</blockquote>
<p>In our example, we're specifying that we're using the <code>Python3.9</code> image.  The image is pulled from the default Docker registry where sharable images are hosted.  The default registry is used when the registry URL is not specified.</p>
<h3 id="heading-workdir">WORKDIR</h3>
<p>The next step is setting the container image's working directory.  The default value is <code>/</code> but it is best practice to set a working directory. In our example, we're creating a directory in our image and setting our working directory to: <code>/code</code>.</p>
<h3 id="heading-copy-and-run-dependencies">COPY and RUN Dependencies</h3>
<p>The Copy statement is straight forward, we're just copying our local file into our working directory or to any other directory in our Docker container image.</p>
<p>We're following best practice to copy the <code>requirements.txt</code> file first before the rest of our project files, let me explain why.  The reason we're only working with dependencies is back to the fact that Docker builds images in layers.  This package dependency layer is usually not changed very often so Docker will cache this layer and avoid re-running the installation of all dependencies each build. </p>
<p>The pip install command is executed next by the RUN command.  Pip is updated and then installs requirements with the <code>--no-cache-dir</code> feature used.  We don't need pip to cache its dependency installations, we'll let Docker be in charge of that.  The install will run the installation of dependencies the first time, then every next image build if no new packages are included or changed in the <code>requirement.txt</code> file then Docker will reuse the cached version.   </p>
<h3 id="heading-copy-app">COPY App</h3>
<p>Now copying the application source files are at the end of the Dockerfile. These files tend to change often so this layer will create itself each time when copying over application files into our container's working directory.</p>
<h3 id="heading-cmd">CMD</h3>
<p>The final step will execute the application.  We're working with the commands that we normally start a FastAPI project with but now in the form of comma separated parameters. We're executing the app object in the app file on localhost port 80. </p>
<h3 id="heading-build-image">Build Image</h3>
<p>Now that we know what's going on in our Dockerfile, we use a specific command to build an image using the file. The command below will build our image.  The <code>-t</code> argument will specify the name of our image. </p>
<pre><code class="lang-bash">docker build -t fastdockerimage .
</code></pre>
<p>The first time you build your image a long list of installation steps are are shown.  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660349341597/tFoxQWEqQc.png" alt="docker-new-build.png" /></p>
<p>The next time, cached layers are used and you notice a much faster build time.  The cached layers are used.  The lengthy python downloads are avoided. The cached lines are shown in the screenshot below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660349358394/-NZMcHzGR.png" alt="docker-new-build-cached.png" /></p>
<h3 id="heading-run-container">RUN Container</h3>
<p>If you're using Docker Desktop, you can see your newly created image as shown below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660349476809/C6nOAFo57.png" alt="docker-image.png" /></p>
<p>With our new Docker image we can now create and run a Docker Container.  </p>
<pre><code class="lang-bash">docker run -d --name fastdockercontainer -p 80:80 fastdockerimage
</code></pre>
<p>We're running the container and specifying the container name as <code>fastdockercontainer</code> running on exposed port 80 (mapped to the internal container port 80) from on our new image called <code>fastdockerimage</code>.  The <code>-d</code> argument indicates detached mode, so you can close your terminal and the terminal will still run.  The Container ID is also printed to the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660349697446/Y0zqn8JC7.png" alt="docker-build-container.png" /></p>
<p>Docker desktop now shows our running container. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660349704490/zGQEF65Xn.png" alt="docker-container.png" /></p>
<p>We can hit the container on localhost port 80 as usual. The application is not just running on a local Uvicorn server on our OS but in fact running on an isolated container environment. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660349945986/9AzYUp7FB.png" alt="docker-localhost.png" /></p>
<p>This article was meant to clarify the basics of running a FastAPI project in Docker.  Future articles will build on these foundational steps to build a real world production level project. </p>
]]></content:encoded></item><item><title><![CDATA[Fast API - SQL Server Connection]]></title><description><![CDATA[Introduction
This article will continue our journey and connect a FastAPI project to a SQL Server database.  We'll walk through a full CRUD (Create, Read, Update, Delete) example of saving data through our FastAPI project to a database.   We'll work ...]]></description><link>https://itsjoshcampos.codes/fast-api-sql-server-connection</link><guid isPermaLink="true">https://itsjoshcampos.codes/fast-api-sql-server-connection</guid><category><![CDATA[Python]]></category><category><![CDATA[SQL Server]]></category><category><![CDATA[Docker]]></category><category><![CDATA[APIs]]></category><category><![CDATA[REST API]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Wed, 13 Apr 2022 19:52:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649802176745/Zdh_l70Gt.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>This article will continue our journey and connect a FastAPI project to a SQL Server database.  We'll walk through a full CRUD (Create, Read, Update, Delete) example of saving data through our FastAPI project to a database.   We'll work with a healthcare example and manage a Patient table.  Of course, we aren't reviewing database design, so we are working with a single table for now.   You should already be familiar with databases and have sufficient access required to create tables and read and insert from a database.</p>
<p>Code for this example can be found here - <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-4-db-crud">API SQL Server DB CRUD Repo</a>. The README provides startup steps.</p>
<p>For this article, I'm working with a local SQL Server 2017 Database on my mac.  Before continuing, if you don't have a SQL Server playground follow <a target="_blank" href="https://itsjoshcampos.codes/run-sql-server-on-macos-docker">my article to set up SQL Server on your mac</a>.  This will give you a fully functioning SQL Server Database running in a Docker container on your local machine.  </p>
<p>We'll also be using SQL Alchemy as the database ORM.  SQL Alchemy is a well-known ORM project to connect SQL databases.  </p>
<h1 id="heading-toolkit">Toolkit</h1>
<ul>
<li><a target="_blank" href="https://fastapi.tiangolo.com/tutorial/sql-databases/">FastAPI - SQL Databases</a></li>
<li><a target="_blank" href="https://docs.sqlalchemy.org/en/14/index.html">SQL Alchemy ORM 1.4</a></li>
<li><a target="_blank" href="https://www.pymssql.org/">PyMsSql</a></li>
<li><a target="_blank" href="https://pydantic-docs.helpmanual.io/">Pydantic Schemas/ Models</a></li>
<li>SQL Server 2017</li>
<li>Docker Desktop</li>
</ul>
<h1 id="heading-database-setup">Database Setup</h1>
<p>At this point, you should already have a SQL Server Db to work with.  In the project repository,  run the <code>Db_Scripts/SEED_Script.sql</code> file to create the Patient Table and add a single record to it.  Running a <code>SELECT</code> statement shows the single record in the table.  My database is named <code>MyTestDb</code>, change any of the SQL Statements to make your database name when necessary.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649816539115/u1wZmPPlT.png" alt="fastapi-db-seed.png" /></p>
<h1 id="heading-fastapi-db-connection">FastAPI Db Connection</h1>
<p>Using SQL Alchemy, we need a <code>connection.py</code> file to setup a database connection.  We'll create this connection as a dependency to use in our API routers.  </p>
<p>First things first, when using SQL Alchemy an <code>engine</code> is required to make connections to databases.</p>
<p>Once the <code>engine</code> is created, we use SQL Alchemy's <code>sessionmaker</code> to create a <code>session</code>.  The <code>session</code> serves as a collection of queries/ statements that we call to execute in the database.  The <code>session</code> will hold these events until the <code>session</code> is instructed to either <code>commit</code> or <code>rollback</code> the events.  </p>
<p>When making a database call in our API, we call a method <code>get_db</code> to execute the following <code>try/catch</code> statement to start a database <code>session</code>.  Once we execute our queries/ instructions in this <code>session</code>, the <code>finally</code> statement will also close the <code>session</code> when it is complete. </p>
<pre><code class="lang-python"><span class="hljs-comment"># taken from utils/connection.py</span>

...

<span class="hljs-comment"># Create engine</span>
engine = create_engine(<span class="hljs-string">f'mssql+pymssql://<span class="hljs-subst">{settings.DB_UID}</span>:<span class="hljs-subst">{settings.DB_PWD}</span>@<span class="hljs-subst">{settings.DB_SERVER}</span>:<span class="hljs-subst">{settings.DB_PORT}</span>/<span class="hljs-subst">{settings.DB_NAME}</span>'</span>)

<span class="hljs-comment"># Create Session</span>
SessionLocal = sessionmaker(autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>, bind=engine)


...


<span class="hljs-keyword">try</span>:
    db = SessionLocal()
    <span class="hljs-keyword">yield</span> db
<span class="hljs-keyword">finally</span>:
    db.close()
</code></pre>
<p>To use these database sessions, we'll import our <code>utils</code> module into the <code>app.py</code> file.  </p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>
<span class="hljs-keyword">import</span> utils
</code></pre>
<p>In our API Endpoints, we insert the database connection as a dependency in the method parameter.  We now have the <code>db</code> instance to call database instructions.  </p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>

<span class="hljs-comment"># CRUD Routes</span>
<span class="hljs-comment"># GET ALL</span>
<span class="hljs-comment"># Response will be a LIST of schema.Patient</span>
<span class="hljs-comment"># The Schema.Patient List instance will be mapped from the model.Patient ORM instance from Sql Alchemy</span>
<span class="hljs-meta">@app.get("/Patient", response_model=List[schema.Patient])</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetAll</span>(<span class="hljs-params">db: Session = Depends(<span class="hljs-params">utils.get_db</span>)</span>):</span>

    <span class="hljs-comment"># get the model.patient with the given id</span>
    <span class="hljs-comment"># using sql alchemy orm, we're querying the Patient table</span>
    query = db.query(model.Patient)
    patients = query.all()

    <span class="hljs-keyword">return</span> patients
</code></pre>
<p>Now have connectivity to our database, we need to discuss creating Models to interact with our database.</p>
<h1 id="heading-models-vs-schemas">Models Vs Schemas</h1>
<h2 id="heading-orm-models-sql-alchemy">ORM Models SQL Alchemy</h2>
<p>We're utilizing two different representations of our Patient table in the API, an ORM Model (SQL Alchemy) and a Schema Model (Pydantic).</p>
<p>The ORM Model is a SQL Alchemy representation of the Patient Table.  This ORM model knows the database table that queries are going to be executed on by the <code>__tablename__= 'Patient'</code> statement.  The ORM model has a direct representation of the database table fields. This assists with validation when trying to save or edit <code>Patient</code> fields.</p>
<p>ORM Models are made from the Delcarative Base SQL Alchemy Class in the <code>utils/connection.py</code> file.  The code to create an ORM model is shown below.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Patient/model.py</span>

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column, Integer, String, DateTime
<span class="hljs-keyword">from</span> utils.connection <span class="hljs-keyword">import</span> Base

<span class="hljs-comment"># Base is coming from the SQL Alchemy Base class created in utils/connection.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Patient</span>(<span class="hljs-params">Base</span>):</span>
    __tablename__ = <span class="hljs-string">'Patient'</span>

    ID = Column(Integer, primary_key=<span class="hljs-literal">True</span>)
    BirthDate = Column(DateTime)
    FirstName = Column(String(<span class="hljs-number">50</span>))
    LastName = Column(String(<span class="hljs-number">50</span>))
    AccountNumber = Column(String(<span class="hljs-number">25</span>))
    Department = Column(String(<span class="hljs-number">20</span>))
    Room = Column(String(<span class="hljs-number">1</span>))
</code></pre>
<h2 id="heading-schema-models-pydantic">Schema Models (Pydantic)</h2>
<p>The Schema models on the other hand are Pydantic models that can be used in the API to also:</p>
<ul>
<li>validate data</li>
<li>identify endpoint <code>response</code> types</li>
<li>be used in the Swagger or Redoc Documentation pages</li>
</ul>
<p>The Schemas are created from the Pydantic <code>BaseModel</code> class, unlike the ORM Models.</p>
<p>Our Patient table has two schemas.  The <code>PatientCreate</code> schema is used as the body request when inserting a new record in our <code>post</code> endpoint.  The <code>ID</code> field is not part of this schema so the API can only validate only the required fields.</p>
<p>The second schema is the same representation of the Patient ORM model.  Type schema has the <code>orm_mode</code> value set to <code>true</code> in order to support mapping this schema to the Patient ORM Model when returning model types.  This is specifically used when we get a Patient or Patient list in our endpoints.  We're using the ORM SQL Alchemy model to return the records from the database.  The data is then validated and returned in the API as a Pydantic model.  This is shown in the endpoint decorater: </p>
<pre><code class="lang-python"><span class="hljs-meta">@app.get("/Patient", response_model=List[schema.  Patient])</span>
</code></pre>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel

<span class="hljs-comment"># Create Patient Schema (Pydantic Model)</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatientCreate</span>(<span class="hljs-params">BaseModel</span>):</span>
    BirthDate: datetime
    FirstName: str
    LastName: str
    AccountNumber: str
    Department: str
    Room: str

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Patient</span>(<span class="hljs-params">BaseModel</span>):</span>
    ID: int
    BirthDate: datetime
    FirstName: str
    LastName: str
    AccountNumber: str
    Department: str
    Room: str

    <span class="hljs-comment"># ORM Mode is used to support models that map to ORM objects, in this case model.Patient (sqlAlchemy)</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>
</code></pre>
<p>Schemas can be used in different scenarios when validating or specifying only specific elements of a class object.  </p>
<h1 id="heading-crud">CRUD</h1>
<h2 id="heading-get-all-read">Get All - READ</h2>
<p>This endpoint will return all Patient records.  No request parameters are included in this endpoint.  Fast API identifies the get method by the decorator <code>@app.get</code> command.  </p>
<p>The route is <code>/Patient</code> and the response is a list of the <code>schema.Patient</code> model. </p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>

<span class="hljs-comment"># GET ALL</span>
<span class="hljs-comment"># Response will be a LIST of schema.Patient</span>
<span class="hljs-comment"># The Schema.Patient List instance will be mapped from the model.Patient ORM instance from Sql Alchemy</span>
<span class="hljs-meta">@app.get("/Patient", response_model=List[schema.Patient])</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetAll</span>(<span class="hljs-params">db: Session = Depends(<span class="hljs-params">utils.get_db</span>)</span>):</span>

    <span class="hljs-comment"># get the model.patient with the given id</span>
    <span class="hljs-comment"># using sql alchemy orm, we're querying the Patient table</span>
    query = db.query(model.Patient)
    patients = query.all()

    <span class="hljs-keyword">return</span> patients
</code></pre>
<h2 id="heading-get-single-read">Get Single - READ</h2>
<p>This endpoint will return a single Patient record.  A single <code>ID</code> parameter is included in this endpoint.  </p>
<p>The route is <code>/Patient/{ID}</code> and the response is a single record of the <code>schema.Patient</code> model.  </p>
<p>If a Patient with the provided <code>ID</code> is not found a <code>404, not found</code> response is returned.</p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>

<span class="hljs-comment"># GET Single</span>
<span class="hljs-comment"># Response will be a single schema.Patient </span>
<span class="hljs-comment"># The Schema.Patient instance will be mapped from the model.Patient ORM instance from Sql Alchemy</span>
<span class="hljs-meta">@app.get("/Patient/{ID}")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetSingle</span>(<span class="hljs-params">ID: int, db: Session = Depends(<span class="hljs-params">utils.get_db</span>)</span>):</span>

    <span class="hljs-comment"># get the patient with the given Patient ID</span>
    query = db.query(model.Patient).filter(model.Patient.ID == ID)
    patient = query.one_or_none()

    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> patient:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">404</span>, detail=<span class="hljs-string">f"Patient with ID <span class="hljs-subst">{ID}</span> not found"</span>)

    <span class="hljs-keyword">return</span> patient
</code></pre>
<h2 id="heading-post-write">Post - Write</h2>
<p>This endpoint will insert a single Patient record.  A request body in JSON format is included in this endpoint.  The request body will need to match the <code>PatientCreate</code> schema specified in the method parameter.</p>
<p>The route is <code>/Patient</code> and the response is a single record of the Patient model that was just inserted into the database.  </p>
<p>After the <code>commit</code> command a <code>refresh</code> command is called.  This returns the Patient model with the newly generated Patient ID. The response will now include the Patient ID which is the primary key value of the Patient.</p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>

<span class="hljs-comment"># POST</span>
<span class="hljs-comment"># Response will be a single schema.Patient after creation in the DB</span>
<span class="hljs-comment"># The Schema.Patient instance will be mapped from the model.Patient ORM instance from Sql Alchemy</span>
<span class="hljs-meta">@app.post("/Patient", response_model=schema.Patient)</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">Post</span>(<span class="hljs-params">patient: schema.PatientCreate, db: Session = Depends(<span class="hljs-params">utils.get_db</span>)</span>):</span>

    <span class="hljs-comment"># create an instance of the model.Patient ORM model from the schema.Patient instance from the request body</span>
    new_patient = model.Patient(BirthDate = patient.BirthDate
                            , FirstName = patient.FirstName
                            , LastName = patient.LastName
                            , AccountNumber = patient.AccountNumber
                            , Department = patient.Department
                            , Room = patient.Room)

    <span class="hljs-comment"># add it to the session and commit it</span>
    db.add(new_patient)
    db.commit()

    <span class="hljs-comment"># update the patient instance to get the newly created Id</span>
    db.refresh(new_patient) 

    <span class="hljs-comment"># return the patient</span>
    <span class="hljs-keyword">return</span> new_patient
</code></pre>
<h2 id="heading-put-write">Put - Write</h2>
<p>This endpoint will update a single Patient record.  A request body in JSON format is included in this endpoint.  The request body will need to match the <code>PatientCreate</code> schema specified in the method parameter.  </p>
<p>The route is <code>/Patient/{ID}</code> and the response is a single record of the Patient model that was just updated into the database.  </p>
<p>A query for the Patient <code>ID</code> is the first step to getting the existing Patient.  Once found, fields are updated based on the request body <code>schema.PatientCreate</code> model.  After the <code>commit</code> statement a <code>refresh</code> command is called.  This returns the Patient model with the newly updated Patient fields. </p>
<p>If a Patient with the provided <code>ID</code> is not found a <code>404, not found</code> response is returned.</p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>

<span class="hljs-comment"># PUT</span>
<span class="hljs-comment"># Response will be a single schema.Patient after creation in the DB</span>
<span class="hljs-comment"># The Schema.Patient instance will be mapped from the model.Patient ORM instance from Sql Alchemy</span>
<span class="hljs-meta">@app.put("/Patient/{ID}", response_model=schema.Patient)</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">Put</span>(<span class="hljs-params">ID: int, patientUpdate: schema.Patient, db: Session = Depends(<span class="hljs-params">utils.get_db</span>)</span>):</span>

    <span class="hljs-comment"># get the model.Patient with the given id</span>
    patient = db.get(model.Patient, ID)

    <span class="hljs-comment"># update patient with the patient from request body (if patient with the given id was found)</span>
    <span class="hljs-keyword">if</span> patient:
        patient.BirthDate = patientUpdate.BirthDate
        patient.FirstName = patientUpdate.FirstName
        patient.LastName = patientUpdate.LastName
        patient.AccountNumber = patientUpdate.AccountNumber
        patient.Department = patientUpdate.Department
        patient.Room = patientUpdate.Room

        db.commit()
        db.refresh(patient)

    <span class="hljs-comment"># check if patient with given id exists. If not, raise exception and return 404 not found response</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> patient:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">404</span>, detail=<span class="hljs-string">f"Patient with ID <span class="hljs-subst">{ID}</span> not found"</span>)

    <span class="hljs-keyword">return</span> patient
</code></pre>
<h2 id="heading-delete-write">Delete - Write</h2>
<p>This endpoint will delete a single Patient record.  A single <code>ID</code> parameter is included in this endpoint.  </p>
<p>The route is <code>/Patient/{ID}</code> and the response a <code>204, No Content</code> upon completion with no errors.</p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>

<span class="hljs-comment"># DELETE</span>
<span class="hljs-meta">@app.delete("/Patient/{ID}", status_code=status.HTTP_204_NO_CONTENT)</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">Delete</span>(<span class="hljs-params">ID: int,  db: Session = Depends(<span class="hljs-params">utils.get_db</span>)</span>):</span>

    <span class="hljs-comment"># get the model.Patient with the given id</span>
    patient = db.get(model.Patient, ID)

    <span class="hljs-comment"># check if patient with given id exists and call delete</span>
    <span class="hljs-keyword">if</span> patient:
        db.delete(patient)
        db.commit()
</code></pre>
<h1 id="heading-end">End</h1>
<p>This article reviewed the steps to set up a SQL Alchemy connection to a SQL Server database.  </p>
<p>This simple CRUD API project shows the steps to read and write data from a SQL Server database table for persistent storage.</p>
]]></content:encoded></item><item><title><![CDATA[Run SQL Server on macOS + Docker]]></title><description><![CDATA[Intro
If you haven't seen Microsoft's latest Docs site update, you really should check them out. There is a getting started article regarding SQL Server (2017) Containers on Linux and other OSs including Azure. The SQL Server 2019 version article is ...]]></description><link>https://itsjoshcampos.codes/run-sql-server-on-macos-docker</link><guid isPermaLink="true">https://itsjoshcampos.codes/run-sql-server-on-macos-docker</guid><category><![CDATA[Databases]]></category><category><![CDATA[SQL Server]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Docker]]></category><category><![CDATA[terminal]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Tue, 05 Apr 2022 20:01:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649188823419/nq0rhUhp8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-intro">Intro</h1>
<p>If you haven't seen Microsoft's latest <a target="_blank" href="https://docs.microsoft.com/en-us/">Docs</a> site update, you really should check them out. There is a getting started article regarding <a target="_blank" href="https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-linux-2017">SQL Server (2017) Containers on Linux</a> and other OSs including Azure. The SQL Server 2019 version article is also available.  This post will go over the detailed steps on setting up a locally running SQL Server (2017) instance on your macOS.</p>
<h1 id="heading-step-1-installing-docker">Step 1: Installing Docker</h1>
<hr />
<blockquote>
<p>(If you have Docker already installed skip to step 2)</p>
</blockquote>
<p>Hopefully, you have a working knowledge of Docker. If you don't, let's walk through the steps of setting up a Docker Desktop for Mac. Download the install from the <a target="_blank" href="https://docs.docker.com/desktop/mac/install/">Docker Store</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649176816715/1YWRpj5gB.png" alt="docker-download.png" /></p>
<p>Based on your chip either Intel or Apple M, download the appropriate <code>.dmg</code> file. Once downloaded, start the <code>.dmg</code> file and drop the file into your Applications folder to install it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649176848409/9yEBLB-7Z.png" alt="docker-install.png" /></p>
<p>Start Docker from your Application folder.  You may need to enter your password for elevated privileges.  Accept the Service Agreement to continue. </p>
<h2 id="heading-configure-docker-preferences">Configure Docker Preferences</h2>
<p>Once Docker is running you'll see the while icon in your toolbar.  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649177161786/XCNCvKjfi.png" alt="docker-preferences.png" /></p>
<p>Microsoft's original article I linked in the intro specifies system preferences as the following:</p>
<ul>
<li><strong>Docker Engine 1.8+</strong></li>
<li><strong>Minimum of 2 GB of disk space</strong><ul>
<li><em>(Common sense sense says you'll disk space will increase with the size of your DB.....)</em></li>
</ul>
</li>
<li><strong>Minimum of 2 GB of RAM</strong><ul>
<li><em>(I prefer to extend to 4GB if you'll be using this heavily in testing and supporting other local apps)</em></li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649177296776/7rnTZ2mmg.png" alt="docker-resources.png" /></p>
<blockquote>
<p>FYI: Any system resource changes will require a Docker Restart.</p>
</blockquote>
<h1 id="heading-step-2-setup-the-sql-container">Step 2: Setup the SQL Container</h1>
<hr />
<p>At this point, Docker is installed. If it isn't running already, double click the Docker App Icon in the Applications folder.</p>
<p>You will see the Docker <em>whale</em> icon in the toolbar. Click on the whale icon and open the dashboard.  A fresh install will have no containers listed.  The green box in the bottom right will indicate your Docker engine is running.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649183693472/IU20W6GcQ.png" alt="docker-dashboard.png" /></p>
<p>With Docker running, let's open a terminal window to execute the next series of commands.</p>
<p>We will pull down the SQL Server container image from the Docker Hub with the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># pull image</span>

docker pull mcr.microsoft.com/mssql/server:2017-latest
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649187419226/5g-D9K1RC.png" alt="docker-pull-image.png" /></p>
<p>Now we need to run the container to set up the SQL Server instance. </p>
<blockquote>
<p>Remember the back slashes below indicate a new line or else you can just type this all on one line without the backslashes.</p>
</blockquote>
<pre><code>sudo docker run <span class="hljs-operator">-</span>e <span class="hljs-string">'ACCEPT_EULA=Y'</span> <span class="hljs-operator">-</span>e <span class="hljs-string">'SA_PASSWORD=MyPassword1'</span> \
   <span class="hljs-operator">-</span>p <span class="hljs-number">1433</span>:<span class="hljs-number">1433</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name LocalSqlServer \
   <span class="hljs-operator">-</span>d mcr.microsoft.com/mssql<span class="hljs-operator">/</span>server:<span class="hljs-number">2017</span><span class="hljs-operator">-</span>latest
</code></pre><p>The command above uses the following flags:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Parameter</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td>-e</td><td>Environment variables for the Docker Container: (1) End user licensing agreement, (2) specify SA Password (REMEMBER THIS!)</td></tr>
<tr>
<td>-p</td><td>Docker host port number (first) and Docker exposed port number (second)</td></tr>
<tr>
<td>--name</td><td>Specify the name of the Docker container. This must be a unique name for each container Docker is running.</td></tr>
<tr>
<td>-d</td><td>Executing the SQL 2017 image</td></tr>
</tbody>
</table>
</div><p>The command will now run the SQL Server container.  Your Docker dashboard will now list the new container with the name you specified in the <code>--name</code> flag. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649187819966/BfzwLwphJ.png" alt="docker-sql-running-dashboard.png" /></p>
<p>From your terminal, the command <code>docker container ls</code> will list the containers in your Docker instance. You can see that the name you specified in the command statement is now listed in your terminal as well..</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649187869164/kaBT4Jl6a.png" alt="docker-sql-running-terminal.png" /></p>
<h1 id="heading-step-3-connecting-to-sql-server">Step 3: Connecting to SQL Server</h1>
<hr />
<p>The database container has been created but we need to get into the container and create our new database.</p>
<h2 id="heading-connect-via-command-line">Connect via Command Line</h2>
<p>Connect to the container:</p>
<p><code>docker exec -it LocalSqlServer "bash"</code></p>
<p>Once in the container, you'll have a <code>:/#</code> prompt.</p>
<p>Now we can connect to the SQL Instance:</p>
<p><code>/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'MyPassword1'</code></p>
<p>You can leave out the <code>-P</code> flag and you will be prompted for the password. Once connected to the SQL instance, you will now have the <code>1&gt;</code> prompt.  A simple <code>SELECT 1+1</code> will return the following: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649188231308/sRSiZ2LxZ.png" alt="docker-connect-sql.png" /></p>
<p>A simple script in the SQLCMD prompt can be tested with the prompt above.
From within the SQLCMD prompt, you can feed any T-SQL statement followed by the <code>GO</code> statement to run the command.</p>
<p>Next steps are to list existing databases.  You will see the default SQL Server databases. We'll create a new DB and than show the newly create <code>MyTestDB</code> show in the database list. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649188450821/8f-kSpCVb.png" alt="docker-create-db.png" /></p>
<h2 id="heading-connect-using-azure-data-studio">Connect Using Azure Data Studio</h2>
<p>You can download <a target="_blank" href="https://docs.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15">Azure Data Studio</a> for macOS to connect to your new SQL Server Database.  </p>
<p>Enter the information below, <code>localhost</code> as your server.  Use <code>SA</code> as your login until you get a new user created and use the default password specified earlier. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649189388002/8t6pfEgEH.png" alt="docker-azure-studio-connect.png" /></p>
<p>You now have a GUI to now interact with, you see our new DB listed below. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649189452085/fjFFiw0yU.png" alt="docket-azure-studio-dbs.png" /></p>
<p>Start and stop the Docker container as you please, when you want to work with SQL Server on your Mac!</p>
<h1 id="heading-end">End</h1>
<hr />
<p>If you followed along with this walkthrough, you:</p>
<ul>
<li>Downloaded and Setup Docker Desktop for Mac</li>
<li>Pulled the SQL Server 2017 Image</li>
<li>Ran and initialized a new SQL Server Database Instance</li>
<li>Connected to the new Docker container and SQL Server Command Prompt to interact with the SQL Server Database</li>
</ul>
<h3 id="heading-reference-links">Reference Links</h3>
<ul>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/">MS Docs</a></li>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker">MS Docs SQL Server on Linux</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Fast API - API Key Authorization]]></title><description><![CDATA[Introduction
The previous article reviewed how to set and read environment variables. We'll use that knowledge and set up an API Key as an environment variable for authorization in a FastAPI project. Keys are not a variable you want to check into you...]]></description><link>https://itsjoshcampos.codes/fast-api-api-key-authorization</link><guid isPermaLink="true">https://itsjoshcampos.codes/fast-api-api-key-authorization</guid><category><![CDATA[Python]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Sat, 02 Apr 2022 01:25:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648272085081/44c9wXFF-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>The previous article reviewed how to set and read environment variables. We'll use that knowledge and set up an API Key as an environment variable for authorization in a FastAPI project. Keys are not a variable you want to check into your code repo, so environment variables are a perfect place for them.</p>
<p>An API Key serves two main purposes, a method of identifying the caller or requestor of the API Service if you're assigning keys and also controlling access to the API Service. It is a simple method of security to protect API resources.</p>
<p>Code for this example can be found <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-3-api-key">here - API Key Project Repo</a>. The README provides startup steps.</p>
<h1 id="heading-setup-an-api-key-environment-variable">Setup an API Key Environment Variable</h1>
<p>For this walkthrough, we will store the API Key in a <code>.env</code> file.</p>
<pre><code class="lang-json"># .env file

API_KEY=<span class="hljs-string">"my file key"</span>
</code></pre>
<h1 id="heading-api-key-middleware">API Key Middleware</h1>
<p>FastAPI will require some middleware in order to process the key and validate it. We'll create a new file <code>auth.py</code> to host the key validation middleware. Let's review the imports in the file.</p>
<p>First off, we're importing the class <code>Settings()</code> and <code>get_settings</code> method which will serve as a cache. These are dependencies for our environment variables. The cache will hold the environment variables read from our <code>.env</code> file.</p>
<p>Additional FastAPI imports are required, <code>APIKeyHeader</code> and <code>Security</code> in order to validate API Keys coming in through the header. The <code>Depends</code> function will load our dependencies like the <code>Settings()</code> cache for our API Key.</p>
<p>The <code>starlette</code> and <code>HTTP Exception</code> imports are used to return forbidden responses and statuses when the API Key is invalid or not included.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Imports in auth.py file</span>

<span class="hljs-keyword">from</span> config <span class="hljs-keyword">import</span> Settings, get_settings

<span class="hljs-keyword">from</span> fastapi.security.api_key <span class="hljs-keyword">import</span> APIKeyHeader
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Security, HTTPException, Depends
<span class="hljs-keyword">from</span> starlette.status <span class="hljs-keyword">import</span> HTTP_403_FORBIDDEN
</code></pre>
<p>To utilize the API Key, the caller or requestor will be required to include the API Key in the Request Header. The API Key will need to be included in the header for all secure requests in our API. The header name, as shown below, is named <code>access_token</code>.</p>
<pre><code class="lang-python">api_key_header = APIKeyHeader(name=<span class="hljs-string">"access_token"</span>, auto_error=<span class="hljs-literal">False</span>)
</code></pre>
<p>The next step is the validation process. When the header is not included or is invalid, then a <code>403</code> status is returned with the message: <code>Could not validate API Key</code>.</p>
<p>Valid API Key requests are forwarded to the original route.</p>
<pre><code class="lang-python"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_api_key</span>(<span class="hljs-params">api_key_header: str = Security(<span class="hljs-params">api_key_header</span>)</span>):</span>
    <span class="hljs-keyword">if</span> api_key_header == config_env[<span class="hljs-string">"API_KEY"</span>]:
        <span class="hljs-keyword">return</span> api_key_header   
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">raise</span> HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail=<span class="hljs-string">"Could not validate API KEY"</span>
        )
</code></pre>
<p>The entire middleware code is shown below:</p>
<pre><code class="lang-python"><span class="hljs-comment"># auth.py</span>

<span class="hljs-keyword">from</span> .config <span class="hljs-keyword">import</span> config_env
<span class="hljs-keyword">from</span> fastapi.security.api_key <span class="hljs-keyword">import</span> APIKeyHeader
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Security, HTTPException
<span class="hljs-keyword">from</span> starlette.status <span class="hljs-keyword">import</span> HTTP_403_FORBIDDEN

api_key_header = APIKeyHeader(name=<span class="hljs-string">"access_token"</span>, auto_error=<span class="hljs-literal">False</span>)

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_api_key</span>(<span class="hljs-params">api_key_header: str = Security(<span class="hljs-params">api_key_header</span>)</span>):</span>
    <span class="hljs-keyword">if</span> api_key_header == config_env[<span class="hljs-string">"API_KEY"</span>]:
        <span class="hljs-keyword">return</span> api_key_header   
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">raise</span> HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail=<span class="hljs-string">"Could not validate API KEY"</span>
        )
</code></pre>
<h1 id="heading-lock-down-routes">Lock Down Routes</h1>
<p>The <code>app.py</code> file will require two new imports to secure the API endpoints. We are importing the <code>APIKey</code> class and <code>auth</code> utility file we created.</p>
<pre><code class="lang-python"><span class="hljs-comment"># New Imports for app.py</span>
<span class="hljs-keyword">from</span> fastapi.security.api_key <span class="hljs-keyword">import</span> APIKey
<span class="hljs-keyword">import</span> auth
</code></pre>
<p>To utilize this middleware in our API, we will assign an API Key dependency to secure our routes. We will include the validation step as a parameter in our routes method. The API Key middleware is a dependency call to <code>auth.get_api_key</code> that is executed before executing the endpoint.</p>
<p>The first route listed below is using the API Key dependency in its route method. Before executing the endpoint, the API Key validation step is performed. If it does not pass this validation sequence, the <code>403</code> response is returned. If the API Key validation is successful, the route endpoint will execute and return the JSON body shown below.</p>
<p>The second route in the example is not locked down. The route can be executed without providing an API Key.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Routes in the app.py file</span>

<span class="hljs-comment"># Lockedown Route</span>
<span class="hljs-meta">@app.get("/secure")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">info</span>(<span class="hljs-params">api_key: APIKey = Depends(<span class="hljs-params">auth.get_api_key</span>)</span>):</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"default variable"</span>: api_key
    }

<span class="hljs-comment"># Open Route</span>
<span class="hljs-meta">@app.get("/open")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">info</span>():</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"default variable"</span>: <span class="hljs-string">"Open Route"</span>
    }
</code></pre>
<h1 id="heading-using-api-key-in-swagger">Using API Key in Swagger</h1>
<p>With the code updates complete, let's see how this works in Swagger.</p>
<p>Start up the server.</p>
<pre><code class="lang-bash">uvicorn app:app --reload
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648860273090/Bu5CggBuR.png" alt="apikey-swagger.png" /></p>
<p>Swagger now has two new UI features.</p>
<ol>
<li><p>The two default routes are listed, only one has the lock icon assigned. The <code>/secure</code> route will require an API Key to be provided and validated before returning API resources.<br /> If you execute the route without providing a valid API Key you will be denied access. In the screenshot below, no API Key was included in the header request and a <code>403 Forbidden</code> response was returned.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648860222318/SQNgSf1e9.png" alt="apikey-denied.png" /></p>
</li>
<li><p>The green Authorize button is the next new feature. You can click on this to enter your secret API Key. This key will now be sent as a header to your API for validation with each request.<br /> Enter your API Key exactly as it exists in your <code>.env</code> file.<br /> Click Authorize and close the window.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648860699199/IQFQVPm-p.png" alt="apikey-token.png" /></p>
<p> If you re-execute the route now that you entered your API Key, you will receive a successful response and see that your API Key was included in the request's header.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648861006621/MLKBTIh7_.png" alt="apikey-success.png" /></p>
</li>
</ol>
<h1 id="heading-end">End</h1>
<p>You now have a working API Key Middleware setup for a FastAPI Project. This example was using a plaintext key but additional steps to "productionize" would be to encrypt the key and add a step to decrypt the key in your <code>auth.py</code> utility file. HTTPS must always be enforced when dealing with keys being sent over the wire.</p>
<p>Individual User Auth can also be applied by utilizing JWT and will be covered in later articles.</p>
]]></content:encoded></item><item><title><![CDATA[Fast API - Environment Variables]]></title><description><![CDATA[Introduction
Recap... the first article gave a simple Hello World setup to get FastAPI up and running.  This article will take the next step and introduce using environment variables in your FastAPI project.
Environment Variables
Environment variable...]]></description><link>https://itsjoshcampos.codes/fast-api-environment-variables</link><guid isPermaLink="true">https://itsjoshcampos.codes/fast-api-environment-variables</guid><category><![CDATA[Python]]></category><category><![CDATA[terminal]]></category><category><![CDATA[APIs]]></category><category><![CDATA[development]]></category><category><![CDATA[variables]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Thu, 31 Mar 2022 18:35:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648683928556/KVTvuXuOA.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Recap... the first article gave a simple <code>Hello World</code> setup to get FastAPI up and running.  This article will take the next step and introduce using environment variables in your FastAPI project.</p>
<h1 id="heading-environment-variables">Environment Variables</h1>
<p>Environment variables are used for specifying external configurations or settings such as: credentials, secrets, important stuff we need to remember, etc. Not just are they important but they can change from time to time, so they need to be in a location for quick access to change and be available for any resource or application that is utilizing them. </p>
<p>First, let's review how we set environment variables on macOS or similar *nix environments. Read my short article on <a target="_blank" href="https://itsjoshcampos.codes/4-options-to-create-environment-variables-on-nix-machines">Environment Variables</a> as a refresher.</p>
<p>Now let's review how to read those environment variables in a FastAPI project. </p>
<h2 id="heading-read-environment-variables-in-fastapi">Read Environment Variables in FastAPI</h2>
<p>Reading environment variables can be done with the <code>os</code> and the <code>dotenv</code> libraries to read both system or file-based variables.  It is more or less a default common practice in Python development.  The example below shows how this is done. </p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> dotenv_values

<span class="hljs-comment"># Python Environment Variable setup required on System or .env file</span>
config_env = {
    **dotenv_values(<span class="hljs-string">".env"</span>),  <span class="hljs-comment"># load local file development variables</span>
    **os.environ,  <span class="hljs-comment"># override loaded values with system environment variables</span>
}

<span class="hljs-comment"># Access the variable like below</span>
<span class="hljs-comment"># print(config_env["VAR_NAME"])</span>
</code></pre>
<p>This is a fine option but not the best option when working with FastAPI.</p>
<p>Let's review how to do this the FastAPI way according to the documentation.</p>
<h2 id="heading-option-1-pyndatic-settings">Option 1 - Pyndatic <code>Settings()</code></h2>
<p>Code for this example can be found <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-2-env">here- Default Option</a>.  The README provides startup steps. </p>
<p>Utilizing the Pydantic Settings Management utility is the recommended option when working with environment variables in a FastAPI project.  This option will walk through creating a global class instance of your environment variables to be shared in your FastAPI project.</p>
<p>This is done by importing Pydantic's <code>BaseSettings</code> and creating a class <code>Settings()</code> just as with any Pydantic model.</p>
<p>We'll follow good standards and create a config file to host our environment variable settings rather than having this clutter up the main <code>app.py</code> file.   When the <code>Settings()</code> class is instantiated, Pydantic will read in the environment variables (case sensitive) for each of the attributes you created in your class. </p>
<p>Default values will be supplied to the class instance if the environment variables do not exist.</p>
<blockquote>
<p>Caution when using default values if you are trying to avoid checking in sensitive information in your code repository.  </p>
</blockquote>
<pre><code class="lang-python"><span class="hljs-comment"># config.py</span>
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseSettings

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>(<span class="hljs-params">BaseSettings</span>):</span>
    DEFAULT_VAR=<span class="hljs-string">"some default string value"</span>  <span class="hljs-comment"># default value if env variable does not exist</span>
    API_KEY: str
    APP_MAX: int=<span class="hljs-number">100</span> <span class="hljs-comment"># default value if env variable does not exist</span>

<span class="hljs-comment"># global instance</span>
settings = Settings()
</code></pre>
<p>With the config in a separate file, you can import it into your main <code>app.py</code> file.  </p>
<p>In our example the route <code>/vars</code> will return the class attributes that were imported.</p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI

<span class="hljs-comment"># import settings for variable access</span>
<span class="hljs-keyword">from</span> config <span class="hljs-keyword">import</span> settings

app = FastAPI()

<span class="hljs-meta">@app.get("/vars")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">info</span>():</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"default variable"</span>: settings.DEFAULT_VAR,
        <span class="hljs-string">"api key"</span>: settings.API_KEY,
        <span class="hljs-string">"app max integer"</span>: settings.APP_MAX,
    }
</code></pre>
<p>I created my python virtual environment and started the server by including the environment variables at invocation.  The <code>API_KEY</code> and <code>APP_MAX</code> are set at invocation but the <code>DEFAULT_VAR</code> will be the default value set up in the Pydantic class because I am not specifying a value for it.  Hitting the API will provide the result below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648748770682/mpm6O6Hp0.png" alt="startup-env-default.png" /></p>
<pre><code><span class="hljs-comment"># Response body for /vars</span>
{
  <span class="hljs-string">"default variable"</span>: <span class="hljs-string">"some default string value"</span>,
  <span class="hljs-string">"api key"</span>: <span class="hljs-string">"SECRETKEY"</span>,
  <span class="hljs-string">"app max integer"</span>: <span class="hljs-number">799</span>
}
</code></pre><h2 id="heading-option-2-environment-variables-using-lru-cache">Option 2 - Environment Variables using LRU Cache</h2>
<p>You can follow along with the following <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-2-env-cache">GitHub Project - Cache Option</a>.  The README provides startup steps. </p>
<p>Instead of creating a global settings instance, another way of reading environment variables is to utilize LRU (Least Recently Used) Cache.  This creates a dependency of <code>Settings()</code> instead of a default instance of <code>settings=Settings()</code>.  For this option, more imports are required: LRU Cache, <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/dependencies/">Depends</a>, and the class <code>Settings()</code>.  Note, that the <code>Settings()</code> class is imported not the instance <code>settings</code>, the global instance is no longer needed for this option.</p>
<pre><code class="lang-python"><span class="hljs-comment"># import </span>
<span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> lru_cache

<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Depends, FastAPI

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

app = FastAPI()

<span class="hljs-comment"># New decorator for cache</span>
<span class="hljs-meta">@lru_cache()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_settings</span>():</span>
    <span class="hljs-keyword">return</span> Settings()

<span class="hljs-comment"># route is now using the Depends feature to import Settings</span>
<span class="hljs-meta">@app.get("/vars")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">info</span>(<span class="hljs-params">settings: Settings = Depends(<span class="hljs-params">get_settings</span>)</span>):</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"default variable"</span>: settings.DEFAULT_VAR,
        <span class="hljs-string">"api key"</span>: settings.API_KEY,
        <span class="hljs-string">"app max integer"</span>: settings.APP_MAX,
    }
</code></pre>
<p>The <code>config.py</code> file change for this option is just removing the created class instance (last line from the option 1).  Everything else is the same.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Updated config.py file</span>
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseSettings

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>(<span class="hljs-params">BaseSettings</span>):</span>
    DEFAULT_VAR=<span class="hljs-string">"some default string value"</span> <span class="hljs-comment"># default value if env variable does not exist</span>
    API_KEY: str
    APP_MAX: int=<span class="hljs-number">100</span> <span class="hljs-comment"># default value if env variable does not exist</span>
</code></pre>
<p>I'm starting the virtual environment and server by including the environment variables at invocation just like before. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648749369803/DBOuA57yz.png" alt="startup-env-cache.png" /></p>
<pre><code><span class="hljs-comment"># Response body for /vars</span>
{
  <span class="hljs-string">"default variable"</span>: <span class="hljs-string">"some default string value"</span>,
  <span class="hljs-string">"api key"</span>: <span class="hljs-string">"SECRETKEY"</span>,
  <span class="hljs-string">"app max integer"</span>: <span class="hljs-number">799</span>
}
</code></pre><h2 id="heading-option-3-environment-variables-in-env-file">Option 3 - Environment Variables in <code>.env</code> File</h2>
<p>You can follow along with the following <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-2-env-file">GitHub Project - Env File Option</a>.  The README provides startup steps. </p>
<p>The last thing we'll cover is to set up environment variables in a local <code>.env</code> file instead of passing them during invocation. I prefer using env files that I can leave in a project.  </p>
<pre><code><span class="hljs-comment"># Example .env file</span>
<span class="hljs-attr">API_KEY</span>=<span class="hljs-string">"my file key"</span>
<span class="hljs-attr">APP_MAX</span>=<span class="hljs-string">"199"</span>
</code></pre><blockquote>
<p>REMEMBER: Add the .env file to your <code>.gitignore</code> file so it won't be checked into your repository. </p>
</blockquote>
<p> Your new <code>.env</code> file is added to the <code>config.py</code> file as part of the class attributes. </p>
<pre><code class="lang-python"><span class="hljs-comment"># config.py</span>
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseSettings


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>(<span class="hljs-params">BaseSettings</span>):</span>
    DEFAULT_VAR=<span class="hljs-string">"some default string value"</span> <span class="hljs-comment"># default value if env variable does not exist</span>
    API_KEY: str
    APP_MAX: int=<span class="hljs-number">100</span> <span class="hljs-comment"># default value if env variable does not exist</span>

<span class="hljs-comment"># specify .env file location as Config attribute</span>
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        env_file = <span class="hljs-string">".env.sample"</span>
</code></pre>
<p>Pydantic will now look explicitly in your file to read in environment variables.  I'm starting the virtual environment and server now with the following command.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648749658109/DOPjwLGR8.png" alt="startup-env-file.png" /></p>
<pre><code><span class="hljs-comment"># Response body for /vars</span>
{
  <span class="hljs-string">"default variable"</span>: <span class="hljs-string">"some default string value"</span>,
  <span class="hljs-string">"api key"</span>: <span class="hljs-string">"my file key"</span>,
  <span class="hljs-string">"app max integer"</span>: <span class="hljs-number">199</span>
}
</code></pre><h1 id="heading-end">End</h1>
<p>The official FastAPI docs are great and explain in further detail the use of LRU Cache and other options around Environment Variables and Settings.  Read them <a target="_blank" href="https://fastapi.tiangolo.com/advanced/settings/#environment-variables">here</a>. </p>
<p>In the next article, we'll review API Key authorization. </p>
]]></content:encoded></item><item><title><![CDATA[4 Options to Create Environment Variables on *nix Machines]]></title><description><![CDATA[Setting Environment Variables
This article is meant as a reminder on how to set up and read environment variables for development.

Python Program Invocation. This option allows for variables to live for the duration of your program.
This works when ...]]></description><link>https://itsjoshcampos.codes/4-options-to-create-environment-variables-on-nix-machines</link><guid isPermaLink="true">https://itsjoshcampos.codes/4-options-to-create-environment-variables-on-nix-machines</guid><category><![CDATA[terminal]]></category><category><![CDATA[os]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Thu, 31 Mar 2022 18:30:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648751338688/v7kN39lMR.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-setting-environment-variables">Setting Environment Variables</h1>
<p>This article is meant as a reminder on how to set up and read environment variables for development.</p>
<ul>
<li><p>Python Program Invocation. <em>This option allows for variables to live for the duration of your program.</em>
This works when starting any Python program regardless of the operating system.  </p>
<pre><code class="lang-bash"><span class="hljs-comment"># Startup with env variables during program invocation</span>
SOME_KEY=<span class="hljs-string">"qwerty123"</span> SOME_PORT=<span class="hljs-string">"99999"</span> myPythonApp.py
</code></pre>
</li>
<li><p>Set via terminal with the <code>EXPORT</code> command.  Environment variables created in your terminal are saved to your terminals session.  They only exist for the duration of your terminal window, once closed they are gone and are not available across multiple terminal windows. 
The screenshot below shows an example of a newly created environment variable <code>SOME_TEXT_VAR</code> created in one session that is not available in another. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648747221247/GvVZ4TUy2.png" alt="env_export_example.png" /></p>
</li>
<li><p>Using a <code>.env</code> file in development project.<br />Add a dot env file to your project to store variables for your project.  Depending on your project you can read in the environment variables in different ways specific to your project. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648747347794/jVI7IGoSK.png" alt="env-file-example.png" /></p>
</li>
<li><p>Create permanent environment variables by modifying your Bash or ZSH (Z) Shell Profiles.  Based on whatever terminal you use, open the following (macOS) <code>~/.bash-profile</code> (bash) or <code>~/.zshrc</code> (ZSH) file in order to create permanent environment variables.  You can open those files in nano, vi, or whatever text editor of your liking.  The <code>#</code> is a comment indicator.  The example below is shown using the ZSH shell in my terminal. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648578374469/qgAMLanbW.png" alt="create_env.png" /></p>
</li>
</ul>
<h2 id="heading-read-environment-variables">Read Environment Variables</h2>
<p>From your terminal, you can print out a single environment variable at a time or all of them at once. </p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-variable">$VAR_NAME</span>
<span class="hljs-comment"># terminal print single variable</span>
</code></pre>
<pre><code class="lang-bash">printenv
<span class="hljs-comment"># print all system environment variables</span>
</code></pre>
<h1 id="heading-end">End</h1>
<p>These are helpful reminders when setting up and accessing environment variables for development.</p>
]]></content:encoded></item><item><title><![CDATA[FAST API - Getting Started]]></title><description><![CDATA[Introduction
This article is the start of a series on how to build an API using the FastAPI Framework.
Environment
This walkthrough will be built on macOS.  However, no reason you can't follow along on Windows as well.  I'll try to provide instructio...]]></description><link>https://itsjoshcampos.codes/fast-api-getting-started</link><guid isPermaLink="true">https://itsjoshcampos.codes/fast-api-getting-started</guid><category><![CDATA[Python]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Fri, 25 Mar 2022 21:47:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648241547011/xs5rWB_tg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>This article is the start of a series on how to build an API using the <a target="_blank" href="https://fastapi.tiangolo.com/">FastAPI</a> Framework.</p>
<h1 id="heading-environment">Environment</h1>
<p>This walkthrough will be built on macOS.  However, no reason you can't follow along on Windows as well.  I'll try to provide instructions for both when possible. </p>
<p>Code for this article can be found in the following <a target="_blank" href="https://github.com/ItsJoshCampos/fast-api-series/tree/main/article-1-getting-started">GitHub Repo</a>.  Each folder will contain a separate project assigned to the assigned article in the series. </p>
<h1 id="heading-tools">Tools</h1>
<ul>
<li>Python 3</li>
<li>macOS</li>
<li>VS Code (Editor) with the Python Extension from Microsoft</li>
</ul>
<h1 id="heading-python-virtual-environments">Python Virtual Environments</h1>
<p>Navigate to a directory of your choosing to set up your new FastAPI Project.  In your new directory follow the next Command Line/ Terminal scripts. </p>
<blockquote>
<p>Virtual Environments are a Python best practice to keep different project modules/ libraries separate from each other.   You can use the <code>pip freeze</code> command to add required libraries to a <code>requirements.txt</code> file.   This allows you to re-install the required libraries for the project at a later time.</p>
</blockquote>
<h2 id="heading-setup-in-windows">Setup in Windows</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># Create Virtual Environment</span>
python3 -m venv venv

<span class="hljs-comment"># Change VS Code Python Interpreter to the VENV python Version</span>

<span class="hljs-comment"># Start Virtual Environment</span>
venv\Scripts\activate.bat

<span class="hljs-comment"># Command Prompt will show running Virtual Environment</span>
</code></pre>
<h2 id="heading-setup-in-macos">Setup in macOS</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># Create Virtual Environment</span>
python3 -m venv venv

<span class="hljs-comment"># Change VS Code Python Interpreter to the VENV python Version</span>

<span class="hljs-comment"># Start Virtual Environment</span>
<span class="hljs-built_in">source</span> venv/bin/activate

<span class="hljs-comment"># Command Prompt will show running Virtual Environment</span>
</code></pre>
<h2 id="heading-install-packages">Install Packages</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># Install FAST API with all dependencies for now</span>
pip install <span class="hljs-string">"FastAPI[all]"</span>
</code></pre>
<p>Running the setup commands should look like the following screenshot.  The activated Virtual Environment is shown at the start of the terminal line <code>venv</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648243693781/kgnF4slZ7.png" alt="Project Setup Commands.png" /></p>
<h1 id="heading-fastapi-hello-world-example">FastAPI Hello World Example</h1>
<p>REF: https://fastapi.tiangolo.com/tutorial/first-steps/</p>
<p>Create a new file named: <code>app.py</code></p>
<pre><code class="lang-python"><span class="hljs-comment"># app.py</span>
<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-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> { <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world"</span> }
</code></pre>
<h1 id="heading-run-the-fastapi-server">Run the FastAPI Server</h1>
<p>From your terminal or integrated terminal in VS Code, run the following commands to start the API server.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Start up the server</span>
<span class="hljs-comment"># First app is the app filename</span>
<span class="hljs-comment"># Second app is the name of the FastAPI instance in the app.py file</span>
<span class="hljs-comment"># --reload is a hot reload flag for the FastAPI Server</span>

uvicorn app:app --reload
</code></pre>
<p>Starting the server will show the running URL and Port. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648243960814/vxdx5j3pT.png" alt="Startup Server.png" /></p>
<p>Visit <code>http://localhost:8000/docs</code> to view the Swagger Documentation for the FastAPI Server. 
The ReDoc interface is also available by default at the following URL: <code>http://localhost:8000/redoc</code>.</p>
<p>The default endpoint in the example below <code>/</code> will return the Hello World payload.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648244161548/Qsw3PFPn0.png" alt="Test Server.png" /></p>
<p>The articles in this series will cover further configurations regarding security, database connections, and more.</p>
]]></content:encoded></item><item><title><![CDATA[Python Lists, Part 2]]></title><description><![CDATA[For Loops
As discussed in Part 1, a simple for loop will iterate through a sequential type value. 
# Iterate through a list or string
name = "josh"
for n in name: 
        i

# OUTPUT:
# 'j'
# 'o'
# 's'
# 'h'

map()
The map() feature offers an altern...]]></description><link>https://itsjoshcampos.codes/python-lists-part-2</link><guid isPermaLink="true">https://itsjoshcampos.codes/python-lists-part-2</guid><category><![CDATA[Python]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Mon, 14 Mar 2022 19:20:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1647228471732/1iF2EBAuc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-for-loops">For Loops</h2>
<p>As discussed in <a target="_blank" href="https://itsjoshcampos.codes/python-lists-part-1">Part 1</a>, a simple <code>for</code> loop will iterate through a sequential type value. </p>
<pre><code class="lang-python"><span class="hljs-comment"># Iterate through a list or string</span>
name = <span class="hljs-string">"josh"</span>
<span class="hljs-keyword">for</span> n <span class="hljs-keyword">in</span> name: 
        i

<span class="hljs-comment"># OUTPUT:</span>
<span class="hljs-comment"># 'j'</span>
<span class="hljs-comment"># 'o'</span>
<span class="hljs-comment"># 's'</span>
<span class="hljs-comment"># 'h'</span>
</code></pre>
<h2 id="heading-map">map()</h2>
<p>The <code>map()</code> feature offers an alternative to looping through a sequence while also applying a function on each traversed element. </p>
<p>The example below will traverse each element in the dictionary and activate the <code>isActive</code> field.  We pass both the function and the array.  </p>
<p>The <code>map()</code> function returns an object. To print the results, we convert the resulting object back to a list using <code>list()</code>. </p>
<p>Lists are reference types so the original list is also updated. </p>
<pre><code class="lang-python">users = [{<span class="hljs-string">"name"</span>: <span class="hljs-string">"Josh"</span>, <span class="hljs-string">"isActive"</span>: <span class="hljs-literal">False</span>}
                    , {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Esmeralda"</span>, <span class="hljs-string">"isActive"</span>: <span class="hljs-literal">False</span>}
                    , {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Daniel"</span>, <span class="hljs-string">"isActive"</span>: <span class="hljs-literal">False</span>}]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">activate_users</span>(<span class="hljs-params">usr</span>):</span>
        usr[<span class="hljs-string">"isActive"</span>] = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">return</span> usr

updated_users = map(activate_users, users)
users_active = list(updated_users) <span class="hljs-comment"># convert object to list</span>

users_active
<span class="hljs-comment"># OUTPUT: </span>
<span class="hljs-comment"># [{'name': 'Josh', 'isActive': True}</span>
<span class="hljs-comment">#   , {'name': 'Esmeralda', 'isActive': True}</span>
<span class="hljs-comment">#   , {'name': 'Daniel', 'isActive': True}]</span>
</code></pre>
<h2 id="heading-list-comprehensions">List Comprehensions</h2>
<p>List comprehensions offer a 'less lines of code' approach but can also get very complicated very quickly and also decrease readability. </p>
<p>The example below will apply the <code>upper()</code> function to each element in the names list.  </p>
<p>List comprehensions return a list.</p>
<pre><code class="lang-python">names = [<span class="hljs-string">"josh"</span>, <span class="hljs-string">"daniel"</span>, <span class="hljs-string">"esmeralda"</span>]
names_upper = [name.upper() <span class="hljs-keyword">for</span> name <span class="hljs-keyword">in</span> names ]
names_upper

<span class="hljs-comment"># OUTPUT:</span>
<span class="hljs-comment"># ['JOSH', 'DANIEL', 'ESMERALDA']</span>
</code></pre>
<p>List comprehensions are composed of three main parts:</p>
<ul>
<li>list: a sequence collection</li>
<li>element: individual element in the list</li>
<li>expression: Action to apply on the element</li>
<li>condition: optional condition to apply to the element</li>
</ul>
<pre><code class="lang-python">result_list = [expression <span class="hljs-keyword">for</span> element <span class="hljs-keyword">in</span> list]
result_list = [expression <span class="hljs-keyword">for</span> element <span class="hljs-keyword">in</span> list (optional) <span class="hljs-keyword">if</span> condition]
</code></pre>
<p>Let's take a look at the <code>map()</code> example above and user a list comprehension to perform the same task.  The output does not have to be converted to a list because a list comprehension's result is a list.  </p>
<pre><code class="lang-python">users = [{<span class="hljs-string">"name"</span>: <span class="hljs-string">"Josh"</span>, <span class="hljs-string">"isActive"</span>: <span class="hljs-literal">False</span>}
                    , {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Esmeralda"</span>, <span class="hljs-string">"isActive"</span>: <span class="hljs-literal">False</span>}
                    , {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Daniel"</span>, <span class="hljs-string">"isActive"</span>: <span class="hljs-literal">False</span>}]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">activate_users</span>(<span class="hljs-params">usr</span>):</span>
        usr[<span class="hljs-string">"isActive"</span>] = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">return</span> usr

updated_users = [activate_users(usr) <span class="hljs-keyword">for</span> usr <span class="hljs-keyword">in</span> users]

updated_users
<span class="hljs-comment"># OUTPUT: </span>
<span class="hljs-comment"># [{'name': 'Josh', 'isActive': True}</span>
<span class="hljs-comment">#   , {'name': 'Esmeralda', 'isActive': True}</span>
<span class="hljs-comment">#   , {'name': 'Daniel', 'isActive': True}]</span>
</code></pre>
<h2 id="heading-list-comprehension-with-conditional">List Comprehension with Conditional</h2>
<pre><code class="lang-python">grades = [<span class="hljs-number">90</span>, <span class="hljs-number">89</span>, <span class="hljs-number">75</span>, <span class="hljs-number">100</span>, <span class="hljs-number">85</span>, <span class="hljs-number">95</span>, <span class="hljs-number">70</span>]

<span class="hljs-comment"># Filter out only a grade that is equal to or more than 90</span>
nineties = [grade <span class="hljs-keyword">for</span> grade <span class="hljs-keyword">in</span> grades <span class="hljs-keyword">if</span> grade &gt;= <span class="hljs-number">90</span>]
nineties

<span class="hljs-comment"># OUTPUT:</span>
<span class="hljs-comment"># [90, 100, 95]</span>

<span class="hljs-comment"># Filter out only a grade that is equal to or more than 90</span>
nineties = [grade <span class="hljs-keyword">if</span> grade &gt;= <span class="hljs-number">90</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span> <span class="hljs-keyword">for</span> grade <span class="hljs-keyword">in</span> grades]
nineties

<span class="hljs-comment"># OUTPUT: </span>
<span class="hljs-comment"># [90, 0, 0, 100, 0, 95]</span>
</code></pre>
<h2 id="heading-set-comprehension">Set Comprehension</h2>
<p>Very similar to list comprehensions but the resulting value is a set, meaning the result will not have duplicates. The format and components of set comprehensions are the same list comprehensions.  Conditional statements are also optional to use in set comprehensions.</p>
<p>The example below removes the duplicate names from the set. </p>
<pre><code class="lang-python">names = <span class="hljs-string">"josh esmeralda daniel esmeralda daniel josh"</span>.split()
names_set = { name <span class="hljs-keyword">for</span> name <span class="hljs-keyword">in</span> names }
names_set

<span class="hljs-comment"># OUTPUT:</span>
<span class="hljs-comment"># {'daniel', 'esmeralda', 'josh'}</span>

type(names_set)
<span class="hljs-comment"># &lt;class 'set'&gt;</span>
</code></pre>
<h2 id="heading-dictionary-comprehension">Dictionary Comprehension</h2>
<p>The last example will cover dictionary comprehensions that share similar properties and functionality as set and list comprehensions. The difference being that the result is a dictionary and a key index is required.</p>
<p>The example below will get a list of elements from the names dictionary.
For each key:value pair, the value is multiplied by 100 and a new dictionary is returned.<br />The original dictionary is not altered.</p>
<pre><code class="lang-python">names = { <span class="hljs-string">"josh"</span>: <span class="hljs-number">.99</span>, <span class="hljs-string">"daniel"</span>: <span class="hljs-number">.10</span>, <span class="hljs-string">"esmeralda"</span>: <span class="hljs-number">.80</span> }

names_dict = { key:value*<span class="hljs-number">100</span> <span class="hljs-keyword">for</span> (key, value) <span class="hljs-keyword">in</span> names.items() }
names_dict

<span class="hljs-comment"># OUTPUT:</span>
<span class="hljs-comment"># {'josh': 99.0, 'daniel': 10.0, 'esmeralda': 80.0}</span>

type(names_dict)
<span class="hljs-comment"># &lt;class 'set'&gt;</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Python Lists, Part 1]]></title><description><![CDATA[This two part series will provide an introduction to using lists and how to work with them in python, from traversing a for loop to using map() and list comprehension strategies. 
Lists
A list in python is a collection of various elements.  A list ca...]]></description><link>https://itsjoshcampos.codes/python-lists-part-1</link><guid isPermaLink="true">https://itsjoshcampos.codes/python-lists-part-1</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Mon, 14 Mar 2022 03:01:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1647226765380/fYRLe_bCM.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This two part series will provide an introduction to using lists and how to work with them in python, from traversing a for loop to using map() and list comprehension strategies. </p>
<h2 id="heading-lists">Lists</h2>
<p>A list in python is a collection of various elements.  A list can be a collection of integers, characters, dictionaries, or all of the above, not to be confused with an array.  An array requires all elements to be of the same type.  </p>
<pre><code class="lang-python"><span class="hljs-comment"># Empty List</span>
empty_list = []

<span class="hljs-comment"># List of integers</span>
number_list = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]

<span class="hljs-comment"># List of strings</span>
alpha_list = [<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-string">"charlie"</span>, <span class="hljs-string">"delta"</span>, <span class="hljs-string">"echo"</span>]

<span class="hljs-comment"># List of dictionaries</span>
dict_list = [{<span class="hljs-string">"id"</span>: <span class="hljs-number">1</span>, <span class="hljs-string">"role"</span>: <span class="hljs-string">"admin"</span>}, {<span class="hljs-string">"id"</span>: <span class="hljs-number">2</span>, <span class="hljs-string">"role"</span>: <span class="hljs-string">"manager"</span>}] 

<span class="hljs-comment"># List of mixed value types</span>
mixed_list = [<span class="hljs-number">1</span>, <span class="hljs-string">"two"</span>, <span class="hljs-number">3</span>, <span class="hljs-literal">False</span>]

<span class="hljs-comment"># check the type</span>
type(number_list)
<span class="hljs-comment"># OUTPUT: &lt;class 'list'&gt;</span>
</code></pre>
<h2 id="heading-index-andamp-length">Index &amp;  Length</h2>
<pre><code class="lang-python"><span class="hljs-comment"># Access list items by a positive index</span>
alpha_list[<span class="hljs-number">1</span>]
<span class="hljs-comment"># OUTPUT: 'beta'</span>

<span class="hljs-comment"># Negative index can also be used to access list elements</span>
number_list[<span class="hljs-number">-2</span>]
<span class="hljs-comment"># OUTPUT: 4</span>

<span class="hljs-comment"># Length of the list</span>
len(alpha_list)
<span class="hljs-comment"># OUTPUT: 5</span>
</code></pre>
<h2 id="heading-slicing">Slicing</h2>
<p>Using indexes, access a subset of a list by using two indexes seperated by a colon <code>[x:y]</code>, x element is inclusive, y element is not.  Omitting the first index, the slice starts at index 0. Omitting the second index, the slice continues until the end of the string.</p>
<pre><code class="lang-python">number_list[<span class="hljs-number">1</span>:<span class="hljs-number">3</span>]
<span class="hljs-comment"># OUTPUT: [2, 3]</span>

number_list[:<span class="hljs-number">4</span>]
<span class="hljs-comment"># OUTPUT: [1, 2, 3, 4]</span>

number_list[<span class="hljs-number">2</span>:]
<span class="hljs-comment"># OUTPUT: [3, 4, 5]</span>

<span class="hljs-comment"># Negative indexes with slicing can also be used</span>
number_list[<span class="hljs-number">-3</span>:]
<span class="hljs-comment"># OUTPUT: [3, 4, 5]</span>

number_list[:<span class="hljs-number">-3</span>]
<span class="hljs-comment"># OUTPUT: [1, 2]</span>
</code></pre>
<h2 id="heading-join-andamp-split">Join &amp; Split</h2>
<p>String variables can be split into a list.  Default delimiter is a space but a character delimeter can be specified. </p>
<pre><code class="lang-python"><span class="hljs-string">"Hello world"</span>.split()
<span class="hljs-comment"># OUTPUT: ['Hello', 'world']</span>

<span class="hljs-string">"Hello world"</span>.split(<span class="hljs-string">'o'</span>)
<span class="hljs-comment"># OUTPUT: ['Hell', ' w', 'rld']</span>

<span class="hljs-comment"># The Join method does the opposite and combines a list into a string</span>
<span class="hljs-string">" "</span>.join([<span class="hljs-string">'Hello'</span>, <span class="hljs-string">'world'</span>])
<span class="hljs-comment"># OUTPUT:  'Hello world'</span>

<span class="hljs-string">"-"</span>.join([<span class="hljs-string">'Hello'</span>, <span class="hljs-string">'world'</span>])
<span class="hljs-comment"># OUTPUT:  'Hello-world'</span>
</code></pre>
<h2 id="heading-mutable">Mutable</h2>
<p>Lists are mutable. </p>
<pre><code class="lang-python"><span class="hljs-comment"># Add elements by appending to the end of the list</span>
alpha_list = [<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>]
alpha_list.append(<span class="hljs-string">"charlie"</span>)

alpha_list
<span class="hljs-comment"># OUTPUT: ['alpha', 'beta', 'charlie']</span>

<span class="hljs-comment">#Insert elements at a specific index</span>
alpha_list.insert(<span class="hljs-number">0</span>, <span class="hljs-string">"delta"</span>)
alpha_list
<span class="hljs-comment">#OUTPUT; ['delta', 'alpha', 'beta', 'charlie']</span>
</code></pre>
<h2 id="heading-remove-delete">Remove/ Delete</h2>
<p>Elements can also be removed/deleted from the list.</p>
<pre><code class="lang-python"><span class="hljs-comment"># del will remove the element at a specific index</span>
<span class="hljs-keyword">del</span> alpha_list[<span class="hljs-number">3</span>]
alpha_list
<span class="hljs-comment">#OUTPUT; ['delta', 'alpha', 'beta']</span>

<span class="hljs-comment"># Remove will remove the first match in the list</span>
<span class="hljs-comment"># If multiple matches exist in the list, not all elements are removed</span>
alpha_list.remove(<span class="hljs-string">'alpha'</span>)
alpha_list
<span class="hljs-comment">#OUTPUT; ['delta', 'beta']</span>
</code></pre>
<h2 id="heading-other-helper-methods">Other Helper Methods</h2>
<pre><code class="lang-python"><span class="hljs-comment"># Reverse the order of the list</span>
alpha_list = [<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>]
alpha_list.reverse()
alpha_list
<span class="hljs-comment"># OUTPUT: ['beta', 'alpha']</span>


<span class="hljs-comment"># Sort the list</span>
alpha_list.sort()
alpha_list
<span class="hljs-comment"># OUTPUT; ['delta', 'beta']</span>
</code></pre>
<h2 id="heading-for-loop">For Loop</h2>
<pre><code class="lang-python"><span class="hljs-comment"># Create a List of grades</span>
grades = [<span class="hljs-number">90</span>, <span class="hljs-number">80</span>, <span class="hljs-number">95</span>, <span class="hljs-number">100</span>, <span class="hljs-number">75</span>]

<span class="hljs-comment"># Iterate over the list and print each individual grade</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> grades:
        print(i)

<span class="hljs-comment"># OUTPUT:</span>
<span class="hljs-comment"># 90</span>
<span class="hljs-comment"># 80</span>
<span class="hljs-comment"># 95</span>
<span class="hljs-comment"># 100</span>
<span class="hljs-comment"># 75</span>

<span class="hljs-comment"># Show indexes for elements in list using the enumerate function in a for loop</span>
alpha_list = [<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-string">"charlie"</span>]
<span class="hljs-keyword">for</span> index, alpha  <span class="hljs-keyword">in</span> enumerate(alpha_list):
        print(<span class="hljs-string">f"The word at index: <span class="hljs-subst">{index}</span> is <span class="hljs-subst">{alpha}</span> "</span>)

<span class="hljs-comment"># OUTPUT: </span>
<span class="hljs-comment"># The word at index: 0 is alpha</span>
<span class="hljs-comment"># The word at index: 1 is beta</span>
<span class="hljs-comment"># The word at index: 2 is charlie</span>
</code></pre>
<p>Part 2 of this series will include the more functional programming concepts such as map() and list comprehension examples.  </p>
]]></content:encoded></item><item><title><![CDATA[macOS Terminal Setup]]></title><description><![CDATA[First things first, I use iTERM2 as my terminal replacement on macOS.
Change macOS Shell to zsh, if it's not set already.

System Preferences
Select Users & Groups
Right click your account and Select 'Advanced Options'. If nothing is happening, you m...]]></description><link>https://itsjoshcampos.codes/macos-terminal-setup</link><guid isPermaLink="true">https://itsjoshcampos.codes/macos-terminal-setup</guid><category><![CDATA[macOS]]></category><category><![CDATA[terminal]]></category><category><![CDATA[Visual Studio Code]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Thu, 30 Dec 2021 22:39:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640901322380/-QcmUmzVW.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>First things first, I use <a target="_blank" href="https://www.iterm2.com">iTERM2</a> as my terminal replacement on macOS.</p>
<h2 id="heading-change-macos-shell-to-zsh-if-its-not-set-already">Change macOS Shell to zsh, if it's not set already.</h2>
<ol>
<li>System Preferences</li>
<li>Select Users &amp; Groups</li>
<li>Right click your account and Select 'Advanced Options'. If nothing is happening, you may need to click on the lock icon in the bottom left corner to allow changes.</li>
<li>Change the Login Shell options to <code>/bin/zsh</code>.</li>
<li>Now that the default shell is set to zsh, you can test your efforts by opening a new terminal window and enter the following:
<code>echo \$SHELL</code>.
The output should be: <code>/bin/zsh</code>.</li>
</ol>
<p>Zsh is configurable by the <code>~/.zshrc</code> file. OH MY ZSH is a framework for managing your zsh configurations. Let's get this installed and see up all the cool stuff.</p>
<h2 id="heading-install-homebrew">Install Homebrew</h2>
<pre><code class="lang-bash">/bin/bash -c <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)</span>"</span>
</code></pre>
<h2 id="heading-install-iterm2-using-homebrew">Install iterm2 using Homebrew</h2>
<pre><code class="lang-bash">brew install --cask iterm2
</code></pre>
<blockquote>
<p>What is the <code>--cask</code> you ask?</p>
<p>“To install, drag this icon…” no more. Homebrew Cask installs macOS apps, fonts and plugins and other non-open source software. <a target="_blank" href="https://github.com/Homebrew/homebrew-cask">Read More</a></p>
</blockquote>
<h2 id="heading-install-git-if-you-dont-have-it-already">Install git, if you don't have it already...</h2>
<pre><code class="lang-bash">brew install git
</code></pre>
<h2 id="heading-install-oh-my-zshhttpohmyzsh">Install <a target="_blank" href="http://ohmyz.sh">Oh My Zsh</a></h2>
<p>The install is as easy as executing the curl statement on the website.</p>
<pre><code class="lang-bash">$ sh -c <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)</span>"</span>
</code></pre>
<p>With the framework installed, open the <code>open ~/.zshrc</code> (to open using TextEdit), <code>code ~/.zshrc</code> (to open using VS Code), or <code>vim ~/.zshrc</code> (to open using Vim) review and modify any zsh properties to your liking.
I'm using the 'itsJCs-agnoster' theme:
<code>ZSH_THEME="itsJCs-agnoster"</code>.</p>
<p>This theme is a fork off the original 'agnoster' theme that I customized for myself. You can clone the theme project from <a target="_blank" href="https://github.com/ItsJoshCampos/itsJCs-agnoster.zsh-theme">github</a>. The downloaded theme must be copied into the hidden .oh-my-zsh home directory in order to read it: <code>~/.oh-my-zsh/themes</code>. </p>
<p>The catch with my theme is that it requires <a target="_blank" href="https://github.com/powerline/fonts">Powerline fonts</a> to support the icons shown in the terminal. The following installation steps on the GitHub page will install the required fonts.</p>
<ol>
<li>Clone
<code>git clone https://github.com/powerline/fonts.git --depth=1</code></li>
<li>Install
<code>cd fonts ./install.sh</code></li>
<li>Clean-up
<code>cd .. rm -rf fonts</code></li>
</ol>
<p>The list of installed fonts is available on the GitHub page.  I like <em>Source Code Pro for Powerline</em></p>
<p>To activate the fonts, you'll need to change the fonts in your iTerms preferences. </p>
<p>Here is a list of my other custom settings:</p>
<blockquote>
<p>You can export your preferences as JSON when you find the settings that suit you best:</p>
</blockquote>
<ul>
<li>Profiles &gt; Text &gt; Font: Operator Mono Lig, 15pt, Medium<ul>
<li>My font of choice, you can use any Ligature Coding font (Operator Mono, Dank Mono, etc.)</li>
</ul>
</li>
<li>Profiles &gt; Text &gt; Font: Enable 'Use Ligatures'</li>
<li>Profiles &gt; Text &gt; Font: Enable 'Use a different font for non-ASCII Text'</li>
<li><p>Profiles &gt; Text &gt; Non-ASCII Font: Source Code Pro for Powerline, 15pt, Medium </p>
<ul>
<li>You will need to install a <a target="_blank" href="https://github.com/Lokaltog/powerline-fonts">Powerline-patched font</a> for this theme to render correctly.</li>
</ul>
</li>
<li><p>Profiles &gt; Text &gt; Font: Vertical Height = 100</p>
</li>
<li>Profiles &gt; Text &gt; Font: Line Height = 100</li>
<li>Profiles &gt; Colors &gt; Basic Colors: Foreground and Selection = #10ff00</li>
<li>Profiles &gt; Colors &gt; Basic Colors: Background = #101010</li>
<li>Profiles &gt; Colors &gt; Cursor Colors: Cursor Guide Enabled, set to #808080</li>
<li>Profiles &gt; Window &gt; Window Appearance &gt; Transparency: ~ 35-40% </li>
<li>Profiles &gt; Window &gt; Settings for New Windows &gt; Style: Fullscreen</li>
<li>Profiles &gt; Session &gt; Miscellaneous: Enable 'Status Bar Enabled'</li>
<li>Profiles &gt; Session &gt; Configure Status Bar: Add CPU, RAM, Network, Current Dir Components</li>
<li>Profiles &gt; Session &gt; Configure Status Bar &gt; Advanced: Background = #000000</li>
<li>Appearance &gt; General &gt; Status Bar Location: Bottom</li>
<li>General &gt; Startup &gt; Window Restoration Policy: Open Default Window Arrangement<ul>
<li>I like a three tile window, I saved the window arrangement and have it open on program start.</li>
</ul>
</li>
<li>Appearance &gt; General &gt; Theme: Dark</li>
</ul>
<h2 id="heading-compatibility">Compatibility</h2>
<p>To test if your terminal and font support it, check that all the necessary characters are supported by copying the following command to your terminal: <code>echo "\ue0b0 \u00b1 \ue0a0 \u27a6 \u2718 \u26a1 \u2699"</code>. The result should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640899869794/QTzZsX-98.png" alt="characters.png" /></p>
<h2 id="heading-what-do-the-icons-mean">What do the icons mean?</h2>
<ul>
<li>If the previous command failed (✘)</li>
<li>Git status<ul>
<li>Dirty working directory (±, color change)</li>
</ul>
</li>
<li>Elevated (root) privileges (⚡)</li>
<li>Job output (⚙)</li>
</ul>
<p>Update export path for node modules in your <code>~/.zshrc</code> file:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#NODE MODULES EXPORT</span>
<span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.npm-packages/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre>
<p><strong>Your npm packages directory may be different. This is a custom path I have them installing into.</strong></p>
<p>Removing username@hostname from command prompt can be done by adding the following to the .zshrc file:</p>
<pre><code class="lang-bash">DEFAULT_USER=`whoami`
<span class="hljs-comment"># Those are back ticks, not single quotes!</span>
</code></pre>
<h2 id="heading-vs-code-integrated-terminal">VS Code Integrated Terminal</h2>
<p>If you use the integrated terminal in VS Code, you will also need to add the Powerline font you used to share the same font compatibility. 
You can add the font by going into VS Code settings and add the Powerline font to the font family section. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640903590128/dGC6yzUsp.png" alt="VS Code Integrated Terminal Font Family Settings" /></p>
<p>The settings can also be exported/ modified via the following ID: </p>
<pre><code>terminal.integrated.fontFamily:  <span class="hljs-string">'Cousine for Powerline'</span>
</code></pre><p>Before font setup:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640903727658/3QjPIVWMW.png" alt="VS Code Integrated Terminal Font Before" /></p>
<p>After font setup:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640903773437/nNzw-zjE1.png" alt="VS Code Integrated Terminal Font Before" /></p>
<p>Enjoy swapping themes, fonts, colors to your liking. </p>
]]></content:encoded></item><item><title><![CDATA[Organize Your macOS Dock]]></title><description><![CDATA[Organize the Clutter
The more shortcut friendly you are, the more busy your macOS dock can become. Adding spacers to your dock can help organize those cluttered icons.
This isn’t a drag and drop GUI hack, but roll up your sleeves for a two line termi...]]></description><link>https://itsjoshcampos.codes/organize-your-macos-dock</link><guid isPermaLink="true">https://itsjoshcampos.codes/organize-your-macos-dock</guid><category><![CDATA[macOS]]></category><dc:creator><![CDATA[Josh Campos]]></dc:creator><pubDate>Wed, 22 Dec 2021 03:34:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640143875193/r0nTifmvs.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-organize-the-clutter">Organize the Clutter</h1>
<p>The more shortcut friendly you are, the more busy your macOS dock can become. Adding spacers to your dock can help organize those cluttered icons.</p>
<p>This isn’t a drag and drop GUI hack, but roll up your sleeves for a two line terminal process that will make your desktop look much cleaner.</p>
<h3 id="heading-step-1-create-the-spacer">Step 1: Create the spacer</h3>
<pre><code>defaults write com.apple.dock persistent<span class="hljs-operator">-</span>apps <span class="hljs-operator">-</span>array<span class="hljs-operator">-</span>add <span class="hljs-string">'{tile-type="spacer-tile";}'</span>
</code></pre><h3 id="heading-step-2-reset-the-dock">Step 2: Reset the Dock</h3>
<pre><code><span class="hljs-attribute">killall</span> Dock
</code></pre><p>The dock service is basically restarted and a newly added spacer will be available. Drag into the location as you please. Repeat both 1 &amp; 2 for any additional spacers.</p>
<h2 id="heading-remove-a-spacer">Remove a Spacer</h2>
<p>You can right click on the empty space and select the “Remove from Dock” option just like any other icon.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640143771342/5u4yxqeiB.png" alt="image.png" /></p>
]]></content:encoded></item></channel></rss>