<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anthony Lannutti</title>
    <description>The latest articles on DEV Community by Anthony Lannutti (@lannuttia).</description>
    <link>https://dev.to/lannuttia</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242668%2Fe713aeb9-b1dd-439a-99fb-d7ae19c40a8a.jpg</url>
      <title>DEV Community: Anthony Lannutti</title>
      <link>https://dev.to/lannuttia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lannuttia"/>
    <language>en</language>
    <item>
      <title>Announcing Starlette Session Middleware</title>
      <dc:creator>Anthony Lannutti</dc:creator>
      <pubDate>Sun, 27 Aug 2023 21:33:15 +0000</pubDate>
      <link>https://dev.to/lannuttia/announcing-starlette-session-middleware-4ih5</link>
      <guid>https://dev.to/lannuttia/announcing-starlette-session-middleware-4ih5</guid>
      <description>&lt;h1&gt;
  
  
  Announcing Starlette Session Middleware
&lt;/h1&gt;

&lt;p&gt;I have started releasing alpha versions of my new Starlette Session Middleware package and plan on releasing a stable version as soon as I settle on the API I want. As of the time of writing, I currently support cookie based session storage, cookie based sessions, as well as JWT and signed tokens that are compatible with Starlette's existing upstream middleware. I fully intend on adding support for authorization header based sessions but that does not exist yet (even though it should be easy to add). The source code can be found &lt;a href="https://github.com/lannuttia/starlette-session-middleware"&gt;here&lt;/a&gt; and the package can be found on PyPI &lt;a href="https://pypi.org/project/starlette-session-middleware/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Philosophy
&lt;/h2&gt;

&lt;p&gt;Starlette Session Middleware is designed to provide sane options for session management out of the box but not lock its consumers into one specific way of doing things. This is accomplished through the concept of specialized "backends". This allows you to mix-and-match behaviors by allowing you to swap out these backends based on how exactly you want sessions to be handled. This also allows developers to easily create custom backends to accomplish their specific goals by implementing the specific backend interface and then providing that custom backend to the middleware. This approach is exceptionally powerful and provides some pretty cool improvements over the existing session middleware solutions that I've found. For example, if you want to use a specific JWT library for encoding/decoding JWTs because you don't want to use PyJWT, you can easily create your own JwtBackend that uses your JWT library of choice and provide that to the middleware instead. There are three kinds of "backends" that exist in Starlette Session Middleware.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Codec Backend&lt;/li&gt;
&lt;li&gt;Storage Backend&lt;/li&gt;
&lt;li&gt;Authorization Backend&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The codec backend specifies how the session information is to be encoded and decoded. The storage backend specifies how the encoded token is supposed to be communicated with the client (usually through the set-cookie header). The authorization backend specifies how the session will be communicated with the backend. This would usually be done through an authorization header or through a session cookie but the backend approach would allow for users to specify custom ways that they want to communicate session information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend Examples
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Codec Backend Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jwt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;jwt.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InvalidTokenError&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.datastructures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Secret&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette_session.middleware.codecbackends.errors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DecodeError&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette_session.middleware.codecbackends.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CodecBackendInterface&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JwtBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CodecBackendInterface&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;algorithm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;InvalidTokenError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;DecodeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to decode value"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Authorization Backend Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt;


&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPConnection&lt;/span&gt;


&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette_session.middleware.authorizationbackends.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AuthorizationBackendInterface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CookieAuthorizationBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthorizationBackendInterface&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session_cookie&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HTTPConnection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_cookie&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Storage Backend Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt;


&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.datastructures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MutableHeaders&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette_session.middleware.storagebackends.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StorageBackendInterface&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CookieBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StorageBackendInterface&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;same_site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"lax"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lax"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;https_only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_age&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session_cookie&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security_flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"httponly; samesite="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;same_site&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;https_only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Secure flag can be used with HTTPS only
&lt;/span&gt;            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security_flags&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"; secure"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{session_cookie}={data}; path={path}; {max_age}{security_flags}"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Max-Age=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;; "&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;security_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security_flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Set-Cookie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{session_cookie}={data}; path={path}; {expires}{security_flags}"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"expires=Thu, 01 Jan 1970 00:00:00 GMT; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;security_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security_flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Set-Cookie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Why
&lt;/h1&gt;

&lt;p&gt;Lately I have been playing around with &lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI&lt;/a&gt; and &lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;. I am looking at these as tools that I might leverage in a future backend migration of a service that I maintain for my day job. The existing service uses JWT based authentication and part of my migration strategy involves moving the creation of these JWTs from the existing service to the new service while still being able to decode and use these JWTs in both services concurrently. Even though there is a tutorial for doing this in FastAPI that exists &lt;a href="https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/"&gt;here&lt;/a&gt;. I thought this was a little messy and that led me to look into starlette middleware for session management. Unfortunately for me, Starlette's session middleware does not support JWTs out of the box so it does not meet my use case. There are third-party starlette session packages that exist too but none of these were very flexible and largly seemed to be copy pasted from the upstream Starlette SessionMiddleware implementation with minor changes to change how tokens are encoded and decoded. This led me to ask the question "why can't there be a more generic plugable session middleware that supports many different ways of encoding/decoding tokens and provides an easy way to support cookie or authorization header based sessions?" The answer I came up with is there is no good reason. That lead me to where I am today.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>fastapi</category>
      <category>programming</category>
    </item>
    <item>
      <title>Breaking Down the Monolith</title>
      <dc:creator>Anthony Lannutti</dc:creator>
      <pubDate>Wed, 26 Oct 2022 04:04:20 +0000</pubDate>
      <link>https://dev.to/lannuttia/breaking-down-the-monolith-klm</link>
      <guid>https://dev.to/lannuttia/breaking-down-the-monolith-klm</guid>
      <description>&lt;p&gt;Very rarely do software engineers start with a clean slate. More often than not, we end up maintaining a Frankenstein code base that has evolved over time into something massive and increasingly difficult to modify in a way that can adapt to business needs quickly. Sometimes it doesn't even have to evolve over time in order to become an unmaintainable mess. It doesn't take very much time neglecting thought about separation of concerns and just throwing newly requested behavior in an existing service to end up with something that's doing too much and is difficult to maintain.&lt;/p&gt;

&lt;p&gt;Usually when software engineers find themselves in this position, their first instinct is to rewrite it all at once and build it better next time. In the context of using a microservice based architecture, I would say that this is not a good strategy though. Rewriting an entire application all at once is extremely dangerous. The time required to rewrite is usually very long and problems will tend to hide themselves until the very end. This leaves you with a very long feedback loop and stakeholders that are unhappy because things will break in new and spectacular ways when you actually deploy things to production; regardless of how thorough you think your testing is.&lt;/p&gt;

&lt;p&gt;My favorite strategy for avoiding the all at once rewrite is to use the strangler pattern. When employing this pattern, I look at the existing monolith and try to determine functionality that could exist as its own service and could have utility as an independently deployable unit. An example of this could be the act of using a templating engine to render and send an email. After identifying the section of the monolith that I would like to carve off, I then develop the future replacement in isolation. I do this while taking full advantage of the tight feedback loop that you can get from employing CI/CD practices and building quality testing into the pipeline. This allows me to learn quickly and safely while always being in a deployable state. At this point, the service is launched darkly and is not actually being used by anything. This is great because this allows me to analyze how this fledgling service will behave in the deployment environments and address potential issues before they impact any users. Once I am confident that what I have is ready for use, I then write the code to replace the old behavior with the new behavior. This is the most dangerous moment of the whole process because this is the only part of the process where the monolith is actually being modified. This is not nearly as dangerous as the full blown rewrite because the amount of time since the mistake would have been introduced should be much smaller. This means that I will have been operating under false assumptions for less time and won't need as much rework if corrections need to be made. As this process is being performed, applications that utilize the monolith can be updated to utilize the new microservices either directly or through something like an API gateway. After the process has been repeated enough times, I will eventually find the old monolith simply acts as a request router and that no applications use it. At this point, I have successfully broken down the monolith.&lt;/p&gt;

&lt;p&gt;Ultimately, this pattern allows me to gradually make architectural improvements to a monolith while keeping things reasonably stable and still delivering features. I have found this to be a good compromise because I get to make architectural improvements and end up with a rich set of loosely coupled microservices; all while still providing the features and stability that are demanded by stakeholders.&lt;/p&gt;

&lt;p&gt;If you have used the strangler pattern or have another favorite pattern for decomposing monoliths, let me know in the comment section below.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>microservices</category>
      <category>patterns</category>
    </item>
    <item>
      <title>You're Doing Microservices Wrong</title>
      <dc:creator>Anthony Lannutti</dc:creator>
      <pubDate>Thu, 20 Oct 2022 05:23:52 +0000</pubDate>
      <link>https://dev.to/lannuttia/youre-doing-microservices-wrong-38hn</link>
      <guid>https://dev.to/lannuttia/youre-doing-microservices-wrong-38hn</guid>
      <description>&lt;p&gt;Before I get too deep into my thoughts on this topic, let me reference some definitions of what microservices are from some of the top players in the microservice space.&lt;/p&gt;

&lt;p&gt;"A microservices architecture is a type of application architecture where the application is developed as a collection of services. It provides the framework to develop, deploy, and maintain microservices architecture diagrams and services independently." - &lt;a href="https://cloud.google.com/learn/what-is-microservices-architecture#section-2"&gt;Google&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Microservices are an architectural and organizational approach to software development where software is composed of small independent services that communicate over well-defined APIs. These services are owned by small, self-contained teams." - &lt;a href="https://aws.amazon.com/microservices/"&gt;Amazon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that I've laid out some definitions, I can explain what I see all too often. I talk to software engineers or a manager of a team of software engineers and they claim to be using microservices. They have services that they have containerized and deploy to some flavor of Kubernetes. While this looks like they're using microservices from a tech stack perspective, I find that they are usually missing something more subtle that is robbing them of being able to realize the benefits of a true microservice architecture.&lt;/p&gt;

&lt;p&gt;What these teams are missing is service independence. All too often what you find when you look closer is a suite of containerized services that are tightly coupled with each other and require lock step deployment. Lock step deployment here refers to the inability to support multiple major versions of a service running simultaneously at the same time. If you can't deploy multiple major versions of a service at the same time then when inevitably you need to make an API breaking change, all consuming services need to update at the same time. I would argue that this trait means that the services are not independent as specified in the definitions that I referenced.&lt;/p&gt;

&lt;p&gt;This is what I would define as a distributed monolith. Don't get me wrong, distributed monoliths can have some of the same valuable traits such as being able to horizontally scale your applications to match with the load. What they fail to provide though is the ability to make changes as fast as the owning team/developer is able to. Changes are only able to be made at the speed of the slowest consumer. This issue can be exacerbated greatly if various team priorities don't line up or conforming to the breaking changes proves to be difficult for that team or developer to accommodate in a reasonable time frame.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why You Should Strive for Immutability</title>
      <dc:creator>Anthony Lannutti</dc:creator>
      <pubDate>Wed, 19 Oct 2022 23:25:52 +0000</pubDate>
      <link>https://dev.to/lannuttia/why-you-should-strive-for-immutability-4kak</link>
      <guid>https://dev.to/lannuttia/why-you-should-strive-for-immutability-4kak</guid>
      <description>&lt;p&gt;Just to get my biases out of the way right off the bat, I am not a fan of object-oriented programming (OOP). I tend to prefer functional programming (FP) whenever it is feasible. I'll go into more detail about why I'm not a fan in a future post but I'll quickly go over what I don't like. In my experience OOP leads to code that is more difficult to create, maintain, parallelize, and test.&lt;/p&gt;

&lt;p&gt;In FP, you love immutability. In fact, immutability is one of the core principles of FP. There are many reasons for this and it has been discussed much more eloquently than I could ever hope to discuss it myself. However, I'm going to attempt to discuss this topic for myself and throw my own spin on it.&lt;/p&gt;

&lt;p&gt;As for my assertion that OOP leads to code that is more difficult to unit test, I would posit that there are 3 kinds of tests. Each kind of test has a different level of difficulty to actually extract value from. The easiest is the type of test that just tries to validate a return value or exception that is being thrown. The second kind of test that is a little more difficult to extract value from is the test that depends on changes in state. The third kind of test that is the most difficult to extract value from is the test that requires valdiating interaction with some external component. Dave Farley created a video some months ago that discusses these kinds of and can be found &lt;a href="https://youtu.be/W40mpZP9xQQ"&gt;here&lt;/a&gt;. When you write code that is immutable, you naturally find yourself writing more unit tests that only test return values and exceptions. This provides more value than the other two forms of testing because you are able to completely ignore implementation details that the consumer of you API ultimately shouldn't need to concern themselves with.&lt;/p&gt;

&lt;p&gt;A while back, I watched an NDC Conference keynote by Kevlin Henney on "Refactoring for Immutability". I highly recommend that anyone who is a software developer watch this talk as I found it extremely impactful on how I view the code that I write. In this keynote, there was one slide that stood out to me over all of the other slides. Stop, read it, process it, let it sink in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ly1GpNlh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://image.slidesharecdn.com/refactoringtoimmutability-180703134457/95/refactoring-to-immutability-24-638.jpg%3Fcb%3D1530625794" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ly1GpNlh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://image.slidesharecdn.com/refactoringtoimmutability-180703134457/95/refactoring-to-immutability-24-638.jpg%3Fcb%3D1530625794" alt="How Mutability and State Sharing Affect the Need for Synchronisation" width="638" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I know this isn't a new concept but I like how it can be visualized here likely because this is reminiscent of&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Root_locus"&gt;root locus&lt;/a&gt; analysis from back in my college day's when I was studying to be an Electrical Engineer.&lt;/p&gt;

&lt;p&gt;When you look at a root locus plot, you can very easily see if that system is stable by looking at the position of the poles. As long as your roots aren't in the right hand (or positive plane), your system is stable. In the case of the slide that I've referenced from Kevlin Henney's keynote, there isn't a whole side of that plane that you need to stay out of. You just want to stay out of the top right quadrant. Why do you want to stay out of that quadrant you ask? Because if you enter that quadrant then you instantly need to concern yourself with locking. We want thing to run in parallel so that we can make things run faster. Locks, however are extremely good at making things run really slow in addition to protecting state that gets mutated. Maintaining code with locks is more difficult than maintaining code without locks so if we can avoid it, we should.&lt;/p&gt;

&lt;p&gt;I'm sure you've reasoned this by now but immutability is the solution to this problem. Notice that much like the root locus plot I mentioned earlier, as long as you stay out of the top right-hand quadrant you don't need to worry about synchronization! You don't even really have to think about it past verifying that the API's that you are using in your parallelized code are thread safe. If the API's that you are utilizing are thread safe and you are not mutating any shared data structures, then there is no way that your other threads or processes can stomp all over whatever each other is trying to do.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
