<?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: Sebastián Ramírez</title>
    <description>The latest articles on DEV Community by Sebastián Ramírez (@tiangolo).</description>
    <link>https://dev.to/tiangolo</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%2F184354%2F061bf799-0a13-411d-8115-b634e8d632dd.JPG</url>
      <title>DEV Community: Sebastián Ramírez</title>
      <link>https://dev.to/tiangolo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiangolo"/>
    <language>en</language>
    <item>
      <title>The Future of Education and Art</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Wed, 17 Aug 2022 10:58:00 +0000</pubDate>
      <link>https://dev.to/tiangolo/the-future-of-education-and-art-4h0e</link>
      <guid>https://dev.to/tiangolo/the-future-of-education-and-art-4h0e</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/the-future-of-education-and-art-4h0e"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo/the-future-of-education-and-art-e515f664b778"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/tree/master/the-future-of-education-and-art"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;The current &lt;strong&gt;traditional education system is somewhat broken&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;jobs&lt;/strong&gt; and requirements based on this system are also somewhat broken. Even more when combined with other arbitrary rules like fixed &lt;strong&gt;years of experience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;societies are at risk&lt;/strong&gt;. If we don't change those things, if we don't do it fast enough, an increasing wave of poverty with more power and economic imbalances could come and do a lot of damage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can fix it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And the &lt;strong&gt;future of education and art go together&lt;/strong&gt;, at least I think they should.&lt;/p&gt;




&lt;p&gt;I normally don't think that whatever I have to say could be important. But people keep asking me for my opinions on random things and making me feel like my ideas are sometimes useful.&lt;/p&gt;

&lt;p&gt;This is one of those opinions I have, normally not so public, and probably controversial. But maybe others could find it useful too, so I'm writing this.&lt;/p&gt;

&lt;p&gt;This is just my point of view. It doesn't represent anyone. And I can also change, adjust, and clarify my own point of view later. But maybe some of these ideas can be useful to others.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Traditional Education System
&lt;/h2&gt;

&lt;p&gt;The traditional education system seems designed to train people to adapt and work in the &lt;strong&gt;industrial revolution&lt;/strong&gt;, in factories and such. That was a long time ago. Go early, dress the same, sit at a table, have a supervisor (teacher) in front of many people, do the same things as others do, etc.&lt;/p&gt;

&lt;p&gt;People are given lots and &lt;strong&gt;lots of information&lt;/strong&gt;, in most cases, &lt;strong&gt;without much purpose&lt;/strong&gt;. I think most things learned and taught should have some purpose, otherwise, it's very difficult to learn them, and even more to apply that knowledge. But anyway, that could be a longer post.&lt;/p&gt;

&lt;p&gt;Good institutions normally can afford very good teachers.&lt;/p&gt;

&lt;p&gt;But these institutions and those teachers are located at &lt;strong&gt;a specific point on earth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Only the people around that point or the ones that can afford to go there can apply to study there. And not many are received because the space, teachers, and resources are limited.&lt;/p&gt;

&lt;p&gt;This ends up being some intrinsic &lt;strong&gt;gatekeeping&lt;/strong&gt;. It's unavoidable, you just can't fit more people in that room of the building.&lt;/p&gt;

&lt;p&gt;But it's not because the knowledge, the teacher's style, or resources should be limited to some people. It's just because the structure doesn't allow more people to access it.&lt;/p&gt;

&lt;p&gt;And then, in that same structure, the teacher has to teach the same class year after year. Even if it's the same information, even if nothing new is added or changed, there's a lot of &lt;strong&gt;repetition&lt;/strong&gt; there.&lt;/p&gt;

&lt;p&gt;Many institutions around the world teach the same concepts and ideas. But they end up coming up with different ways to do it. Some can afford to make &lt;strong&gt;better materials&lt;/strong&gt;, methods, or ways of teaching, but that's kept for only that institution. Other institutions keep teaching with the same old resources as before, even if that was improved somewhere else.&lt;/p&gt;

&lt;p&gt;And in some cases, well regarded institutions &lt;strong&gt;teach old or somewhat obsolete concepts&lt;/strong&gt;, or even privative tools that students will never be able to use again (e.g. a programming language), maybe just because the institution had a partnership with the provider of that tool. And when they go out and start working, they end up having to use a completely different tool, and learn it on the job either way.&lt;/p&gt;

&lt;p&gt;But still, people end up wanting to move to some city to be able to attend some school, but the information or learnings are not necessarily tied to that particular place, it's just a coincidence.&lt;/p&gt;

&lt;p&gt;Of course, some professions need hands-on physical experience, like doctors. But many professions are not tightly coupled to space, even more in technology and digital professions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Education Systems Change Lives
&lt;/h3&gt;

&lt;p&gt;I'm not against education, and I know from very close people that these same education systems can and do &lt;strong&gt;change lives&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When people have the choice of studying there or nothing, the difference of education is massive.&lt;/p&gt;

&lt;p&gt;And many have the fortune of having &lt;strong&gt;amazing teachers&lt;/strong&gt; that guide them and help them a lot. But that's not always the case.&lt;/p&gt;

&lt;p&gt;And in some cases, some teachers that want and try to be better, to give practical materials and learnings, are stopped by the mechanics of what is expected in the system, maybe even just because it's easier for the administration to keep things unchanged.&lt;/p&gt;

&lt;p&gt;And also, some education institutions are doing a big job to &lt;strong&gt;evolve and adapt&lt;/strong&gt; to these new changes, which is amazing.&lt;/p&gt;

&lt;p&gt;I'm not against any of that. I only think education systems need to &lt;strong&gt;evolve faster&lt;/strong&gt;, we need to evolve faster, and find ways to teach faster, better, in more accessible ways, more dynamically. Because technology and advancements are not gonna wait for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology and Job Displacement
&lt;/h2&gt;

&lt;p&gt;Technology has &lt;strong&gt;displaced jobs&lt;/strong&gt; for a long time. Old jobs disappear, slowly or quickly, and new jobs appear. Old jobs are replaced by new ones.&lt;/p&gt;

&lt;p&gt;The education systems have to (at least should) adapt to this and teach the new jobs and skills.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;technology moves fast&lt;/strong&gt;, and maybe gets faster every time. And the speed at which technology can alter jobs is higher than the speed at which traditional education systems evolve.&lt;/p&gt;

&lt;p&gt;Traditional education systems tend to expect very long periods dedicated only to &lt;strong&gt;absorbing new knowledge&lt;/strong&gt;, in many cases without much purpose or practice, and then they expect that the next periods will be dedicated to &lt;strong&gt;applying that information&lt;/strong&gt;, without acquiring new information.&lt;/p&gt;

&lt;p&gt;But jobs related to or altered by technology make that idea of one long period studying and one long period applying &lt;strong&gt;less practical&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Technology changes fast. If you start studying something expecting it to take as long as traditional education systems would, by the time you finish, there might be some better alternative. But if you expect not to study again, you will not learn that new better alternative.&lt;/p&gt;

&lt;p&gt;I think it would make more sense to &lt;strong&gt;start studying and start applying as soon as possible&lt;/strong&gt;. And &lt;strong&gt;continue studying and applying forever&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Arguments against technology (e.g. against artificial intelligence) tend to include &lt;strong&gt;job displacement&lt;/strong&gt;. Because for people that have been doing the same thing for a long time and don't have a way to change, update, and adapt, getting their job displaced would be very, very bad.&lt;/p&gt;

&lt;p&gt;It's common to hear "artificial intelligence will replace us" and alarming things like that. But it's not a new problem, artificial intelligence is just technology, and technology has been changing jobs forever.&lt;/p&gt;

&lt;p&gt;Technology, artificial intelligence, automation, are all &lt;strong&gt;just tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's like a &lt;strong&gt;knife&lt;/strong&gt;. The first knife used to cut meat was probably very useful, it was probably a great invention, and it surely helped people a lot then. But then a knife could also be used to kill someone. That doesn't necessarily make the knife itself a bad thing that should be avoided, but people should be taught to use it so that more people use it to improve lives than to damage them.&lt;/p&gt;

&lt;p&gt;With technology we could have the power to reduce the need for many hazardous jobs, repetitive and sometimes numbing jobs, increase efficiency for everyone. We could have the power to increase the food production, reduce waste, reduce energy consumption. &lt;strong&gt;We could solve a lot of the problems in our society and our planet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But then, we come again to the &lt;strong&gt;current traditional education systems&lt;/strong&gt;. If people can't access the information easily, if it's not made to be easy to understand and apply, if it doesn't have a clear purpose, then we don't have a way to help people overcome the challenges from fast-moving technology, to get new jobs, to take advantage of those improvements in technology.&lt;/p&gt;

&lt;p&gt;And powers or billionaires with more control of technology would just get more power, money and control of technology, while the rest gets less, and the imbalance would just get worse.&lt;/p&gt;

&lt;p&gt;It could go really bad, or &lt;strong&gt;we could make it go well for everyone&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Job Requirements
&lt;/h2&gt;

&lt;p&gt;The next problem is the traditional system of how to &lt;strong&gt;hire people&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The process of hiring people tends to be based on &lt;strong&gt;very old practices&lt;/strong&gt; and assumptions that look more and more &lt;strong&gt;absurd&lt;/strong&gt; every day.&lt;/p&gt;

&lt;p&gt;If a job requires someone to have a &lt;strong&gt;specific degree&lt;/strong&gt; from the traditional education system, that is gatekeeping and discarding anyone that could have learned the same things or more with alternative methods (it would certainly discard me, I was homeschooled all my life 😅).&lt;/p&gt;

&lt;p&gt;And knowing that traditional education systems are not being able to evolve and adapt as fast as technology, &lt;strong&gt;why require someone to have a specific degree&lt;/strong&gt; when that is not really what they are gonna need in the job that is evolving at the pace of technology?&lt;/p&gt;

&lt;p&gt;A possible answer is that "they will have the foundational concepts of some topic". But then, the requirement tends to be "a degree on X". But they never examine the topics taught in that "X" career at that particular institution to see if the person knows what is needed. So, why such a strong emphasis in a particular degree when there's really no emphasis in the actual knowledge or skills?&lt;/p&gt;

&lt;h3&gt;
  
  
  Years of Experience
&lt;/h3&gt;

&lt;p&gt;The same goes for &lt;strong&gt;years of experience&lt;/strong&gt;. It's just a proxy indicator for the skill level, but it's quite a bad proxy.&lt;/p&gt;

&lt;p&gt;One person could have 5 or 10 years working with the same technology, doing the same thing day after day. They are not learning something new. And the skills they can get in all that time are not that much.&lt;/p&gt;

&lt;p&gt;And another person could work at a startup where they have to learn new skills very fast, take new challenges, even lead colleagues, and &lt;strong&gt;acquire the equivalent of "10 years of experience" in 2 years&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Companies and job requirements have to be updated as well, to check for actual skills, or at least better indicators of those skills, but &lt;strong&gt;less focus on particular degrees and years of experience&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Governments
&lt;/h3&gt;

&lt;p&gt;Of course, &lt;strong&gt;governments could be greatly improved&lt;/strong&gt; as well. They tend to be the slowest of them all. And many things in governments are still very tightly coupled to traditional and old structures, like traditional education system degrees.&lt;/p&gt;

&lt;p&gt;But they are probably the most difficult to change, and they will slowly evolve and will have to adapt by force at some point.&lt;/p&gt;

&lt;p&gt;That's a much harder battle, we can focus on the things we can have an impact on right now, because we need changes to start right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Education Systems
&lt;/h2&gt;

&lt;p&gt;Coming back to education systems, there are new ones. I'm not inventing anything new here.&lt;/p&gt;

&lt;p&gt;The internet with articles, documentation, videos, massive online courses (in many cases free), provide a way for people around the world to access the &lt;em&gt;same&lt;/em&gt; content.&lt;/p&gt;

&lt;p&gt;Grading can be done by software online. It's not great, but it's not much worse than tests in traditional education systems, and can be scaled much better. The topic of how to test knowledge and the emphasis on those standard educational tests can also be greatly improved, but that's also another longer post.&lt;/p&gt;

&lt;p&gt;Having these resources available online helps a lot with the limit and gatekeeping of &lt;strong&gt;physical locations&lt;/strong&gt; on the planet. It also helps with the problem of the limited seats in a given room in a building, &lt;strong&gt;the internet doesn't have limited seats&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even if the classes are done by the same teachers in person, accessing and using the best materials available online can help a lot.&lt;/p&gt;

&lt;p&gt;But one of the best parts of all this is that the best teachers in the world can give their &lt;strong&gt;best classes once&lt;/strong&gt;, record them, and everyone else could access them. No need for the teachers to work a lot more, at least it's not proportional to the people that can access the information.&lt;/p&gt;

&lt;p&gt;And the &lt;strong&gt;content can be improved and refined&lt;/strong&gt; by them or even others through time. And the best ways to teach something can emerge and improve. So that people can learn things easily and fast, with &lt;strong&gt;less effort&lt;/strong&gt; every time, so more people can learn those things instead of having knowledge reserved only for the most brilliant ones that were able to disentangle a concept from a dry explanation.&lt;/p&gt;

&lt;p&gt;Someone that I admire highly seems to have realized all this, several years ago. Andrew Ng was pioneering work on artificial intelligence, he probably realized all the job displacement it could produce, and that the next problem to solve for society would be &lt;strong&gt;education&lt;/strong&gt; and access to it. And he went and founded Coursera, an online education platform. I think something similar happened with Sebastian Thrun and Udacity. And probably the same with edX and others too.&lt;/p&gt;

&lt;h3&gt;
  
  
  How New Education Systems Affected Me
&lt;/h3&gt;

&lt;p&gt;I'm from Colombia, in Latin America, I was homeschooled all my life, I didn't go to the university (actually, not even to elementary school).&lt;/p&gt;

&lt;p&gt;But I had access (sometimes) to the internet. I was able to download pages to read them offline and learn about web technologies and Wikipedia articles.&lt;/p&gt;

&lt;p&gt;And then when these online course platforms appeared (and I had stable internet), I was able to take &lt;strong&gt;the same classes&lt;/strong&gt; from well regarded institutions that people in those places could, otherwise I wouldn't have been able to.&lt;/p&gt;

&lt;p&gt;Here's an interesting detail. I'm &lt;strong&gt;not against education institutions&lt;/strong&gt;, not even traditional ones. The courses I studied were mostly from those same institutions. I'm &lt;strong&gt;against the system that enforces limiting information&lt;/strong&gt; to only a few people based on arbitrary coincidences (location) and rules, and implicitly gatekeeping that knowledge, when making it more accessible doesn't cost more and can help much more people.&lt;/p&gt;

&lt;p&gt;In those courses, I was taught &lt;strong&gt;very complex concepts&lt;/strong&gt; in ways that seemed easy to understand, at times, I even felt smart. I studied the same concepts in different courses, through time, I also saw other people studying and struggling with the same concepts. And that let me see that &lt;strong&gt;some ways of teaching&lt;/strong&gt; something were &lt;strong&gt;better than others&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In some cases the same concept &lt;strong&gt;could look much more complex&lt;/strong&gt; than when it was explained differently. &lt;strong&gt;The way something is explained makes a huge difference&lt;/strong&gt;. It makes a difference in the effort needed to learn, the speed at which something can be learned, the ability to apply the concept afterwards, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Art for Education
&lt;/h2&gt;

&lt;p&gt;At this point is where &lt;strong&gt;art&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;A possible definition of art would be &lt;strong&gt;the use of aesthetics and imagination to communicate something&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It doesn't have to be jolly, you could have a very &lt;strong&gt;sad and powerful&lt;/strong&gt; piece of art, a song, a painting. But it uses aesthetics, sensation, imagination, to communicate that sadness, in a powerful way.&lt;/p&gt;

&lt;p&gt;And education could be thought of as &lt;strong&gt;the communication of useful information that can be applied&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, art would be one of the most powerful ways to communicate in general (if not just &lt;strong&gt;the most&lt;/strong&gt;). And education is also just about communicating things well.&lt;/p&gt;

&lt;p&gt;I think &lt;strong&gt;art could and should be used for education&lt;/strong&gt;, to teach things.&lt;/p&gt;

&lt;p&gt;How many things are taught to kids via songs? Have you heard the saying that "an image is worth a thousand words"?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Art can accelerate the process of learning, teaching, and education in general.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And now that each piece of content, each resource that is made, can be available online to millions of people around the world, making art to explain a concept is worth it.&lt;/p&gt;

&lt;p&gt;Before, making a piece of art, only for one class, given to a handful of people, might not have been worth the effort, time, money. But if you can teach millions, then all that investment is paid through time and people learning.&lt;/p&gt;

&lt;p&gt;There's also the strange coincidence that artists tend to have a difficult life, it's not easy to succeed, at least economically, via arts. It tends to work for only a handful that takes all the fame and money, but it doesn't work well for the great majority.&lt;/p&gt;

&lt;p&gt;But if art is not dedicated only to entertainment, but also to &lt;strong&gt;education&lt;/strong&gt;, there are a lot of artists that can help, that resource is already there, and doing that would of course help them. Some hundreds or thousands of dollars for some work that can then be replicated for a long time with many people can be very fruitful for the thing that is being taught.&lt;/p&gt;

&lt;p&gt;I imagine courses (mostly online, because those are the ones I've taken the most), where from time to time, they hire a new illustrator, or 3D artist, that makes a new animation that explains the concept that otherwise you would have to just imagine. Or musicians making short songs to teach ideas or things that need to be memorized, etc.&lt;/p&gt;

&lt;p&gt;As part of that, there are also many things that improve how something is taught that I would consider art, or somewhat art at least. Like cues that bring the attention to some content (e.g. some piece of code), diagrams, tools to show and order the information in a beautiful way, etc. Making sure that something is amenable, that it's &lt;strong&gt;easy to communicate&lt;/strong&gt;, is what ensures that it will have the intended effect, that it will be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Small Example Using Art for Education
&lt;/h2&gt;

&lt;p&gt;I have been playing with this idea for a while. Some years ago I took an online course on edX from Berkeley about artificial intelligence. It was &lt;strong&gt;full of illustrations&lt;/strong&gt;, and it was clear that the illustrator knew the concepts, because the illustrations were clear, easy to understand, and accurate. I didn't even think the course was "easy", but it was so &lt;strong&gt;enjoyable&lt;/strong&gt;! And the concepts were much &lt;strong&gt;easier and fast to understand&lt;/strong&gt;. I studied some of the same concepts in several other courses, and the difference between having clear illustrations and art and having only dense descriptions was really big.&lt;/p&gt;

&lt;p&gt;I wanted to have &lt;strong&gt;illustrations for FastAPI&lt;/strong&gt; too. So I recently hired &lt;a href="https://ketrinadrawsalot.tumblr.com/"&gt;Ketrina&lt;/a&gt;, the same illustrator from that edX course, and asked her to make some illustrations for the documentation explaining &lt;a href="https://fastapi.tiangolo.com/async/#concurrency-and-burgers"&gt;concurrency and parallelism in FastAPI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would hope that would help explain the concepts better and more easily.&lt;/p&gt;

&lt;p&gt;I would hope others could also start using art to explain things better, more easily, and faster, and hopefully, &lt;strong&gt;making education more accessible to everyone&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hey! 👋 I'm Sebastián Ramírez (&lt;a href="https://tiangolo.com"&gt;tiangolo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow me, contact me, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;LinkedIn: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev: tiangolo.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/"&gt;Medium: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.com"&gt;Web: tiangolo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>education</category>
      <category>art</category>
      <category>jobs</category>
    </item>
    <item>
      <title>HTTPS for Developers</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Tue, 28 Sep 2021 08:27:34 +0000</pubDate>
      <link>https://dev.to/tiangolo/https-for-developers-1774</link>
      <guid>https://dev.to/tiangolo/https-for-developers-1774</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/https-for-developers-1774"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/https-for-developers-5e42dcf7d4db" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/https-for-developers/README.md" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastapi.tiangolo.com/deployment/https/" rel="noopener noreferrer"&gt;The FastAPI docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Here's a brief introduction to &lt;strong&gt;HTTPS for developers&lt;/strong&gt;. 🔒&lt;/p&gt;

&lt;p&gt;This article is extracted from the &lt;a href="https://fastapi.tiangolo.com/deployment/https/" rel="noopener noreferrer"&gt;FastAPI docs about HTTPS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I just upgraded those docs with several explanations and diagrams, and I thought the end result is &lt;strong&gt;generic&lt;/strong&gt; and &lt;strong&gt;useful&lt;/strong&gt; enough for many other developers (even in other &lt;strong&gt;languages&lt;/strong&gt; and &lt;strong&gt;frameworks&lt;/strong&gt;) to also publish it as a post, so here it is. 🤓&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Is This For
&lt;/h2&gt;

&lt;p&gt;If you are a &lt;strong&gt;user&lt;/strong&gt;, your only interaction with HTTPS is with the &lt;strong&gt;browser&lt;/strong&gt; opening URLs, then you are better off just reading &lt;a href="https://howhttps.works/" rel="noopener noreferrer"&gt;How HTTPS Works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are a &lt;strong&gt;cryptography researcher&lt;/strong&gt;, you are better off studying the cryptographic primitives and then reading the standards (RFCs).&lt;/p&gt;

&lt;p&gt;But if you are a &lt;strong&gt;developer&lt;/strong&gt; (programmer, coder) and want to know &lt;strong&gt;enough technical details&lt;/strong&gt; to understand how it works and &lt;strong&gt;how to use HTTPS&lt;/strong&gt; in your applications without going into the depths of cryptography and web standards, then this is for you! 🎉👇&lt;/p&gt;

&lt;h2&gt;
  
  
  About HTTPS
&lt;/h2&gt;

&lt;p&gt;It is easy to assume that HTTPS is something that is just "enabled" or not.&lt;/p&gt;

&lt;p&gt;But it is way more complex than that.&lt;/p&gt;

&lt;p&gt;To &lt;strong&gt;learn the basics of HTTPS&lt;/strong&gt;, from a consumer perspective, check &lt;a href="https://howhttps.works/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://howhttps.works/" rel="noopener noreferrer"&gt;https://howhttps.works/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, from a &lt;strong&gt;developer's perspective&lt;/strong&gt;, here are several things to have in mind while thinking about HTTPS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For HTTPS, &lt;strong&gt;the server&lt;/strong&gt; needs to &lt;strong&gt;have "certificates"&lt;/strong&gt; generated by a &lt;strong&gt;third party&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;Those certificates are actually &lt;strong&gt;acquired&lt;/strong&gt; from the third party, not "generated".&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Certificates have a &lt;strong&gt;lifetime&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;They &lt;strong&gt;expire&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;And then they need to be &lt;strong&gt;renewed&lt;/strong&gt;, &lt;strong&gt;acquired again&lt;/strong&gt; from the third party.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The encryption of the connection happens at the &lt;strong&gt;TCP level&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;That's one layer &lt;strong&gt;below HTTP&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;So, the &lt;strong&gt;certificate and encryption&lt;/strong&gt; handling is done &lt;strong&gt;before HTTP&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;TCP doesn't know about "domains"&lt;/strong&gt;. Only about IP addresses.

&lt;ul&gt;
&lt;li&gt;The information about the &lt;strong&gt;specific domain&lt;/strong&gt; requested goes in the &lt;strong&gt;HTTP data&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;strong&gt;HTTPS certificates&lt;/strong&gt; "certify" a &lt;strong&gt;certain domain&lt;/strong&gt;, but the protocol and encryption happen at the TCP level, &lt;strong&gt;before knowing&lt;/strong&gt; which domain is being dealt with.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;By default&lt;/strong&gt;, that would mean that you can only have &lt;strong&gt;one HTTPS certificate per IP address&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;No matter how big your server is or how small each application you have on it might be.&lt;/li&gt;
&lt;li&gt;There is a &lt;strong&gt;solution&lt;/strong&gt; to this, however.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;There's an &lt;strong&gt;extension&lt;/strong&gt; to the &lt;strong&gt;TLS&lt;/strong&gt; protocol (the one handling the encryption at the TCP level, before HTTP) called &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Server_Name_Indication" rel="noopener noreferrer"&gt;SNI&lt;/a&gt;&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;This SNI extension allows one single server (with a &lt;strong&gt;single IP address&lt;/strong&gt;) to have &lt;strong&gt;several HTTPS certificates&lt;/strong&gt; and serve &lt;strong&gt;multiple HTTPS domains/applications&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For this to work, a &lt;strong&gt;single&lt;/strong&gt; component (program) running on the server, listening on the &lt;strong&gt;public IP address&lt;/strong&gt;, must have &lt;strong&gt;all the HTTPS certificates&lt;/strong&gt; in the server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;After&lt;/strong&gt; obtaining a secure connection, the communication protocol is &lt;strong&gt;still HTTP&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;The contents are &lt;strong&gt;encrypted&lt;/strong&gt;, even though they are being sent with the &lt;strong&gt;HTTP protocol&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;It is a common practice to have &lt;strong&gt;one program/HTTP server&lt;/strong&gt; running on the server (the machine, host, etc.) and &lt;strong&gt;managing all the HTTPS parts&lt;/strong&gt;: receiving the &lt;strong&gt;encrypted HTTPS requests&lt;/strong&gt;, sending the &lt;strong&gt;decrypted HTTP requests&lt;/strong&gt; to the actual HTTP application running in the same server (the &lt;strong&gt;FastAPI&lt;/strong&gt; application, in this case), take the &lt;strong&gt;HTTP response&lt;/strong&gt; from the application, &lt;strong&gt;encrypt it&lt;/strong&gt; using the appropriate &lt;strong&gt;HTTPS certificate&lt;/strong&gt; and sending it back to the client using &lt;strong&gt;HTTPS&lt;/strong&gt;. This server is often called a &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" rel="noopener noreferrer"&gt;TLS Termination Proxy&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some of the options you could use as a TLS Termination Proxy are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traefik (that can also handle certificate renewals)&lt;/li&gt;
&lt;li&gt;Caddy (that can also handle certificate renewals)&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;HAProxy&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Encrypt
&lt;/h2&gt;

&lt;p&gt;Before Let's Encrypt, these &lt;strong&gt;HTTPS certificates&lt;/strong&gt; were sold by trusted third parties.&lt;/p&gt;

&lt;p&gt;The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive.&lt;/p&gt;

&lt;p&gt;But then &lt;strong&gt;&lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;Let's Encrypt&lt;/a&gt;&lt;/strong&gt; was created.&lt;/p&gt;

&lt;p&gt;It is a project from the Linux Foundation. It provides &lt;strong&gt;HTTPS certificates for free&lt;/strong&gt;, in an automated way. These certificates use all the standard cryptographic security, and are short-lived (about 3 months), so the &lt;strong&gt;security is actually better&lt;/strong&gt; because of their reduced lifespan.&lt;/p&gt;

&lt;p&gt;The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates.&lt;/p&gt;

&lt;p&gt;The idea is to automate the acquisition and renewal of these certificates so that you can have &lt;strong&gt;secure HTTPS, for free, forever&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTPS for Developers by Example
&lt;/h2&gt;

&lt;p&gt;Here's an example of how an HTTPS API could look like, step by step, paying attention mainly to the ideas important for developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain Name
&lt;/h3&gt;

&lt;p&gt;It would probably all start by you &lt;strong&gt;acquiring&lt;/strong&gt; some &lt;strong&gt;domain name&lt;/strong&gt;. Then, you would configure it in a DNS server (possibly your same cloud provider).&lt;/p&gt;

&lt;p&gt;You would probably get a cloud server (a virtual machine) or something similar, and it would have a fixed &lt;strong&gt;public IP address&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the DNS server(s) you would configure a record (an "&lt;code&gt;A record&lt;/code&gt;") to point &lt;strong&gt;your domain&lt;/strong&gt; to the public &lt;strong&gt;IP address of your server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You would probably do this just once, the first time, when setting everything up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: This Domain Name part is way before HTTPS, but as everything depends on the domain and the IP address, it's worth mentioning it here.&lt;/p&gt;

&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;Now let's focus on all the actual HTTPS parts.&lt;/p&gt;

&lt;p&gt;First, the browser would check with the &lt;strong&gt;DNS servers&lt;/strong&gt; what is the &lt;strong&gt;IP for the domain&lt;/strong&gt;, in this case, &lt;code&gt;someapp.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The DNS servers would tell the browser to use some specific &lt;strong&gt;IP address&lt;/strong&gt;. That would be the public IP address used by your server, that you configured in the DNS servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps01.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TLS Handshake Start
&lt;/h3&gt;

&lt;p&gt;The browser would then communicate with that IP address on &lt;strong&gt;port 443&lt;/strong&gt; (the HTTPS port).&lt;/p&gt;

&lt;p&gt;The first part of the communication is just to establish the connection between the client and the server and to decide the cryptographic keys they will use, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps02.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This interaction between the client and the server to establish the TLS connection is called the &lt;strong&gt;TLS handshake&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  TLS with SNI Extension
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Only one process&lt;/strong&gt; in the server can be listening on a specific &lt;strong&gt;port&lt;/strong&gt; in a specific &lt;strong&gt;IP address&lt;/strong&gt;. There could be other processes listening on other ports in the same IP address, but only one for each combination of IP address and port.&lt;/p&gt;

&lt;p&gt;TLS (HTTPS) uses the specific port &lt;code&gt;443&lt;/code&gt; by default. So that's the port we would need.&lt;/p&gt;

&lt;p&gt;As only one process can be listening on this port, the process that would do it would be the &lt;strong&gt;TLS Termination Proxy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The TLS Termination Proxy would have access to one or more &lt;strong&gt;TLS certificates&lt;/strong&gt; (HTTPS certificates).&lt;/p&gt;

&lt;p&gt;Using the &lt;strong&gt;SNI extension&lt;/strong&gt; discussed above, the TLS Termination Proxy would check which of the TLS (HTTPS) certificates available it should use for this connection, using the one that matches the domain expected by the client.&lt;/p&gt;

&lt;p&gt;In this case, it would use the certificate for &lt;code&gt;someapp.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps03.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The client already &lt;strong&gt;trusts&lt;/strong&gt; the entity that generated that TLS certificate (in this case Let's Encrypt, but we'll see about that later), so it can &lt;strong&gt;verify&lt;/strong&gt; that the certificate is valid.&lt;/p&gt;

&lt;p&gt;Then, using the certificate, the client and the TLS Termination Proxy &lt;strong&gt;decide how to encrypt&lt;/strong&gt; the rest of the &lt;strong&gt;TCP communication&lt;/strong&gt;. This completes the &lt;strong&gt;TLS Handshake&lt;/strong&gt; part.&lt;/p&gt;

&lt;p&gt;After this, the client and the server have an &lt;strong&gt;encrypted TCP connection&lt;/strong&gt;, this is what TLS provides. And then they can use that connection to start the actual &lt;strong&gt;HTTP communication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that's what &lt;strong&gt;HTTPS&lt;/strong&gt; is, it's just plain &lt;strong&gt;HTTP&lt;/strong&gt; inside a &lt;strong&gt;secure TLS connection&lt;/strong&gt; instead of a pure (unencrypted) TCP connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Notice that the encryption of the communication happens at the &lt;strong&gt;TCP level&lt;/strong&gt;, not at the HTTP level.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTPS Request
&lt;/h3&gt;

&lt;p&gt;Now that the client and server (specifically the browser and the TLS Termination Proxy) have an &lt;strong&gt;encrypted TCP connection&lt;/strong&gt;, they can start the &lt;strong&gt;HTTP communication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, the client sends an &lt;strong&gt;HTTPS request&lt;/strong&gt;. This is just an HTTP request through an encrypted TLS connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps04.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Decrypt the Request
&lt;/h3&gt;

&lt;p&gt;The TLS Termination Proxy would use the encryption agreed to &lt;strong&gt;decrypt the request&lt;/strong&gt;, and would transmit the &lt;strong&gt;plain (decrypted) HTTP request&lt;/strong&gt; to the process running the application (for example a process with Uvicorn running the FastAPI application).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps05.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Response
&lt;/h3&gt;

&lt;p&gt;The application would process the request and send a &lt;strong&gt;plain (unencrypted) HTTP response&lt;/strong&gt; to the TLS Termination Proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps06.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTPS Response
&lt;/h3&gt;

&lt;p&gt;The TLS Termination Proxy would then &lt;strong&gt;encrypt the response&lt;/strong&gt; using the cryptography agreed before (that started with the certificate for &lt;code&gt;someapp.example.com&lt;/code&gt;), and send it back to the browser.&lt;/p&gt;

&lt;p&gt;Next, the browser would verify that the response is valid and encrypted with the right cryptographic key, etc. It would then &lt;strong&gt;decrypt the response&lt;/strong&gt; and process it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps07.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The client (browser) will know that the response comes from the correct server because it is using the cryptography they agreed using the &lt;strong&gt;HTTPS certificate&lt;/strong&gt; before.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Applications
&lt;/h3&gt;

&lt;p&gt;In the same server (or servers), there could be &lt;strong&gt;multiple applications&lt;/strong&gt;, for example, other API programs or a database.&lt;/p&gt;

&lt;p&gt;Only one process can be handling the specific IP and port (the TLS Termination Proxy in our example) but the other applications/processes can be running on the server(s) too, as long as they don't try to use the same &lt;strong&gt;combination of public IP and port&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps08.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That way, the TLS Termination Proxy could handle HTTPS and certificates for &lt;strong&gt;multiple domains&lt;/strong&gt;, for multiple applications, and then transmit the requests to the right application in each case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificate Renewal
&lt;/h3&gt;

&lt;p&gt;At some point in the future, each certificate would &lt;strong&gt;expire&lt;/strong&gt; (about 3 months after acquiring it).&lt;/p&gt;

&lt;p&gt;And then, there would be another program (in some cases it's another program, in some cases it could be the same TLS Termination Proxy) that would talk to Let's Encrypt, and renew the certificate(s).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftiangolo%2Fblog-posts%2Fraw%2Fmaster%2Fhttps-for-developers%2Fimg%2Fhttps.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;TLS certificates&lt;/strong&gt; are &lt;strong&gt;associated with a domain name&lt;/strong&gt;, not with an IP address.&lt;/p&gt;

&lt;p&gt;So, to renew the certificates, the renewal program needs to &lt;strong&gt;prove&lt;/strong&gt; to the authority (Let's Encrypt) that it indeed &lt;strong&gt;"owns" and controls that domain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To do that, and to accommodate different application needs, there are several ways it can do it. Some popular ways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modify some DNS records&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;For this, the renewal program needs to support the APIs of the DNS provider, so, depending on the DNS provider you are using, this might or might not be an option.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Run as a server&lt;/strong&gt; (at least during the certificate acquisition process) on the public IP address associated with the domain.

&lt;ul&gt;
&lt;li&gt;As we said above, only one process can be listening on a specific IP and port.&lt;/li&gt;
&lt;li&gt;This is one of the reasons why it's very useful when the same TLS Termination Proxy also takes care of the certificate renewal process.&lt;/li&gt;
&lt;li&gt;Otherwise, you might have to stop the TLS Termination Proxy momentarily, start the renewal program to acquire the certificates, then configure them with the TLS Termination Proxy, and then restart the TLS Termination Proxy. This is not ideal, as your app(s) will not be available during the time that the TLS Termination Proxy is off.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;All this renewal process, while still serving the app, is one of the main reasons why you would want to have a &lt;strong&gt;separate system to handle HTTPS&lt;/strong&gt; with a TLS Termination Proxy instead of just using the TLS certificates with the application server directly (e.g. Uvicorn).&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Having &lt;strong&gt;HTTPS&lt;/strong&gt; is very important, and quite &lt;strong&gt;critical&lt;/strong&gt; in most cases. Most of the effort you as a developer have to put around HTTPS is just about &lt;strong&gt;understanding these concepts&lt;/strong&gt; and how they work.&lt;/p&gt;

&lt;p&gt;But once you know the basic information of &lt;strong&gt;HTTPS for developers&lt;/strong&gt; you can easily combine and configure different tools to help you manage everything in a simple way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;This article is extracted from the &lt;a href="https://fastapi.tiangolo.com/deployment/https/" rel="noopener noreferrer"&gt;FastAPI documentation about deployments and HTTPS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to learn concrete examples of some tools you can use and how to configure them to deploy a FastAPI application, check out the next chapters in the &lt;a href="https://fastapi.tiangolo.com/deployment/" rel="noopener noreferrer"&gt;FastAPI documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hey! 👋 I'm Sebastián Ramírez (&lt;a href="https://tiangolo.com" rel="noopener noreferrer"&gt;tiangolo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow me, contact me, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo" rel="noopener noreferrer"&gt;GitHub: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo" rel="noopener noreferrer"&gt;Twitter: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/" rel="noopener noreferrer"&gt;LinkedIn: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev: tiangolo.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/" rel="noopener noreferrer"&gt;Medium: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.com" rel="noopener noreferrer"&gt;Web: tiangolo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>https</category>
      <category>fastapi</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Future of FastAPI and Pydantic is Bright</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Sat, 19 Jun 2021 14:19:37 +0000</pubDate>
      <link>https://dev.to/tiangolo/the-future-of-fastapi-and-pydantic-is-bright-3pbm</link>
      <guid>https://dev.to/tiangolo/the-future-of-fastapi-and-pydantic-is-bright-3pbm</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/the-future-of-fastapi-and-pydantic-is-bright-3pbm"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/the-future-of-fastapi-and-pydantic-is-bright-2d1785a603a9"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/the-future-of-fastapi-and-pydantic-is-bright/README.md"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  In very short
&lt;/h2&gt;

&lt;p&gt;The future of &lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI&lt;/a&gt; and &lt;a href="https://pydantic-docs.helpmanual.io/"&gt;Pydantic&lt;/a&gt; is bright. ✨&lt;/p&gt;

&lt;p&gt;This is because we all, as the Python community, define their future. To help us and to help others. From the Core Developers making Python itself to the new developers that started learning Python this month.&lt;/p&gt;

&lt;p&gt;And as long as these tools are helping us all solve problems, help ourselves, help others, and be more efficient and productive,  we all will keep them working and improving.&lt;/p&gt;

&lt;p&gt;And that's what we are all doing. 🤓🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;You might have heard not long ago about PEP 563, PEP 649, and some changes that could affect Pydantic and FastAPI in the future.&lt;/p&gt;

&lt;p&gt;If you read about it, I wouldn't expect you to understand what all that meant. I didn't fully understand it until I spent hours reading all the related content and doing multiple experiments.&lt;/p&gt;

&lt;p&gt;It might have worried you and maybe confuse you a bit.&lt;/p&gt;

&lt;p&gt;Now there's nothing to be worried about. But still, here I want to help clarify all that and give you a bit more context.&lt;/p&gt;

&lt;p&gt;Brace yourself, you are about to learn a bit more about how Python works, how FastAPI and Pydantic work, how type annotations work, and more. 👇&lt;/p&gt;

&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Start with a basic FastAPI app
&lt;/h3&gt;

&lt;p&gt;FastAPI is based on Pydantic. Let's see a simple example using them both.&lt;/p&gt;

&lt;p&gt;Imagine that we have a file &lt;code&gt;./main.py&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;uvicorn&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&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;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/items/"&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;create_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&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;item&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could run this example and start the API application with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python ./main.py
&lt;span class="go"&gt;
INFO:     Started server process [4418]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you could open your browser and interact with the API docs at &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;But here we want to focus on what happens behind the scenes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Instead of using the last two lines, you could have used the &lt;code&gt;uvicorn&lt;/code&gt; command, and that's what you would normally do. But for this example, it will be useful to see everything from the point of view of the &lt;code&gt;python&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Python works
&lt;/h3&gt;

&lt;p&gt;By running that command above, you are asking your system to start the program called &lt;code&gt;python&lt;/code&gt;. And to give it the file &lt;code&gt;main.py&lt;/code&gt; as a parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In Windows, the program might be called &lt;code&gt;python.exe&lt;/code&gt; instead of just &lt;code&gt;python&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That program called &lt;code&gt;python&lt;/code&gt; (or &lt;code&gt;python.exe&lt;/code&gt;) is written in another programming language called "C". Maybe you knew that.&lt;/p&gt;

&lt;p&gt;And what that program &lt;code&gt;python&lt;/code&gt; does is read the file &lt;code&gt;main.py&lt;/code&gt;, interpret the code that we wrote in it using the &lt;strong&gt;Python&lt;/strong&gt; Programming Language, and execute it step by step.&lt;/p&gt;

&lt;p&gt;So, we have two things with more or less the same name "python" that represent something slightly different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;python&lt;/code&gt;: the program that runs our code (which is actually written in the &lt;strong&gt;C&lt;/strong&gt; programming language)&lt;/li&gt;
&lt;li&gt;"Python": the name of the programming language we use to write our code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, you could say that &lt;code&gt;python&lt;/code&gt; (the program) can read &lt;strong&gt;Python&lt;/strong&gt; (the programming language).&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Runtime
&lt;/h3&gt;

&lt;p&gt;Now, when that program &lt;code&gt;python&lt;/code&gt; is executing our code written in the &lt;strong&gt;Python&lt;/strong&gt; programming language, we call that "runtime".&lt;/p&gt;

&lt;p&gt;It's just the period of time when it is executing our code.&lt;/p&gt;

&lt;p&gt;When our code is not being executed, for example, when we are editing the file &lt;code&gt;./main.py&lt;/code&gt;, it is not &lt;em&gt;running&lt;/em&gt;, so we are not at &lt;strong&gt;runtime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The way that program works is that, at runtime (when our code is being executed), Pydantic and FastAPI read those &lt;strong&gt;type annotations&lt;/strong&gt; (or &lt;strong&gt;type hints&lt;/strong&gt;) to extract their data and do things with it.&lt;/p&gt;

&lt;p&gt;So, for example, in the &lt;code&gt;Item&lt;/code&gt; class above, we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&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;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &lt;strong&gt;runtime&lt;/strong&gt;, Pydantic and FastAPI will see that &lt;code&gt;name&lt;/code&gt; is a &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;price&lt;/code&gt; is a &lt;code&gt;float&lt;/code&gt;. And if we send a JSON request with a &lt;code&gt;price&lt;/code&gt; that is not a &lt;code&gt;float&lt;/code&gt;, they will be able to validate the data for us.&lt;/p&gt;

&lt;p&gt;FastAPI and Pydantic are written in pure &lt;strong&gt;Python&lt;/strong&gt;. How can these tools do that? &lt;strong&gt;Python&lt;/strong&gt; is so powerful that it has features to allow exactly that, to read type annotations at runtime from the same &lt;strong&gt;Python&lt;/strong&gt; code. And Pydantic and FastAPI take advantage of those features.&lt;/p&gt;

&lt;p&gt;Another term commonly used to refer to doing things at &lt;strong&gt;runtime&lt;/strong&gt; is to do things &lt;strong&gt;dynamically&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Static Analysis
&lt;/h3&gt;

&lt;p&gt;The counterpart of &lt;strong&gt;runtime&lt;/strong&gt; would be &lt;strong&gt;static&lt;/strong&gt;. It just means that the code is not being executed. It's treated just as a text file containing code.&lt;/p&gt;

&lt;p&gt;In many cases, "static" is used when saying &lt;strong&gt;Static Analysis&lt;/strong&gt;, &lt;strong&gt;Static Checking&lt;/strong&gt;, &lt;strong&gt;Static Type Checking&lt;/strong&gt;, etc. It refers to tools that understand the rules of the &lt;strong&gt;Python&lt;/strong&gt; Programming Language and that can analyze the code, but that doesn't execute the code itself.&lt;/p&gt;

&lt;p&gt;These tools for &lt;strong&gt;static analysis&lt;/strong&gt; can check if the code is following the rules correctly, checking that the code is valid, providing autocompletion, and other features. When you are editing code and your editor shows a squiggly red line with an error somewhere, that is &lt;strong&gt;static analysis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In some cases, the code could be valid, but it would still be incorrect. For example, if you try to add a &lt;code&gt;str&lt;/code&gt; and a &lt;code&gt;float&lt;/code&gt; together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Rick"&lt;/span&gt;
&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.99&lt;/span&gt;

&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In terms of the rules of the language itself, the code is valid, all the quotes are where they should be, the equal signs are correctly placed, etc. But this code is still incorrect and will not work because you can't add a &lt;code&gt;str&lt;/code&gt; with a &lt;code&gt;float&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Many editors will be able to show you a very valuable squiggly red line with the error message under &lt;code&gt;name + price&lt;/code&gt; that might save you hours debugging. That is also &lt;strong&gt;static analysis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some tools that do &lt;strong&gt;static analysis&lt;/strong&gt; and that you might have heard of are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mypy&lt;/code&gt;&lt;/strong&gt;, the official and main &lt;strong&gt;Static Type Checker&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;flake8&lt;/code&gt;&lt;/strong&gt;, checks for style and correctness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;black&lt;/code&gt;&lt;/strong&gt;, autoformats the code in a consistent way that improves efficiency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyCharm&lt;/strong&gt;, one of the most popular Python editors, has internal components that do &lt;strong&gt;static analysis&lt;/strong&gt; to check for errors, provide autocompletion, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code&lt;/strong&gt;, the other of the most popular Python editors, using Pylance, also has internal tools to do &lt;strong&gt;static analysis&lt;/strong&gt; to check for errors, provide autocompletion, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools have saved tons of development hours by detecting many bugs earlier in the development process and in the exact place where those errors happened. I bet that in many cases you might have seen the red line, realize what the error is, think "ah, yeah, right", fix it, and not even consider that &lt;em&gt;there was a bug in your code&lt;/em&gt;, even for some seconds. If I counted all the times these tools have saved me from these bugs, I would get overwhelmed quickly. 😅&lt;/p&gt;

&lt;p&gt;And if you have ever added type annotations to a code base that didn't have them before, you probably would have seen lots of broken sections in the code base and broken corner cases, that were suddenly obvious and you could then fix them. I surely have.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type Annotations in Python
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Type Annotations&lt;/strong&gt; (also called &lt;strong&gt;Type Hints&lt;/strong&gt;) that we have available in all the supported modern Python versions (Python 3.6 and above) were designed to improve all that &lt;strong&gt;static analysis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The original intention was to allow &lt;code&gt;mypy&lt;/code&gt; and others to help developers while &lt;em&gt;writing&lt;/em&gt; the code. And that was the main focus for a while.&lt;/p&gt;

&lt;p&gt;But then tools like &lt;code&gt;dataclasses&lt;/code&gt; (from the standard library) and &lt;a href="https://twitter.com/samuel_colvin"&gt;Samuel Colvin&lt;/a&gt;'s Pydantic started using these type annotations to do more than only &lt;strong&gt;static analysis&lt;/strong&gt;, and to use these same &lt;strong&gt;type annotations&lt;/strong&gt; at &lt;strong&gt;runtime&lt;/strong&gt;. In the case of Pydantic, to extract that information to do data conversion, validation, and documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type Annotations with Forward References
&lt;/h3&gt;

&lt;p&gt;Now, imagine we have a class (it could be a Pydantic model) like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;child&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have a &lt;code&gt;Person&lt;/code&gt; that could have a child, that would also be a &lt;code&gt;Person&lt;/code&gt;. It all looks fine, right?&lt;/p&gt;

&lt;p&gt;But now when we run the code (or with the help of some static analysis in editors) we will see that we declared &lt;code&gt;child: Optional[Person]&lt;/code&gt; inside the body of the class &lt;code&gt;Person&lt;/code&gt;. So, when that part of the code is run by &lt;code&gt;python&lt;/code&gt;, the &lt;code&gt;Person&lt;/code&gt; inside of &lt;code&gt;name: Optional[Person]&lt;/code&gt; doesn't exist yet (that class is still being created).&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;Forward Reference&lt;/strong&gt;. And it would make the code break.&lt;/p&gt;

&lt;p&gt;And again, the main purpose of these type annotations was to help with &lt;strong&gt;static analysis&lt;/strong&gt;. Using them at runtime was not yet an important use case.&lt;/p&gt;

&lt;p&gt;And having the code break just because we are trying to improve static analysis would be very annoying.&lt;/p&gt;

&lt;p&gt;To overcome that problem, it's also valid to declare that internal &lt;code&gt;Person&lt;/code&gt; as a literal string, like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;child&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="s"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looked weird to me when I discovered it. It's the name of a class just put there inside a string. But it's valid.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;python&lt;/code&gt; is running, it will see that as a literal string, so it will not break.&lt;/p&gt;

&lt;p&gt;And most static analysis tools know this is valid and will read the literal string and understand that it actually refers to the &lt;code&gt;Person&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;By knowing that the &lt;code&gt;Optional["Person"]&lt;/code&gt; actually refers to the &lt;code&gt;Person&lt;/code&gt; class, static analysis tools can, for example, detect that this would be an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Beth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A smart editor will use its static analysis tools to detect that &lt;code&gt;parent.child = 3&lt;/code&gt; is an error because it expects a &lt;code&gt;Person&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This solves the problem of the forward reference in the code and allows us to still use static analysis tools.&lt;/p&gt;

&lt;p&gt;...we are not talking about using these type annotations at &lt;strong&gt;runtime&lt;/strong&gt; yet, but we'll get there later.&lt;/p&gt;

&lt;h3&gt;
  
  
  PEPs in Python
&lt;/h3&gt;

&lt;p&gt;PEP stands for &lt;strong&gt;Python Enhancement Proposal&lt;/strong&gt;. A PEP is a technical document describing changes to Python, additions to the standard library (for example, adding &lt;code&gt;dataclasses&lt;/code&gt;), and other types of changes. Or in some cases, they just provide information and establish conventions.&lt;/p&gt;

&lt;p&gt;The name says &lt;strong&gt;Proposal&lt;/strong&gt;, but when they are finally accepted they become a standard.&lt;/p&gt;

&lt;h3&gt;
  
  
  PEP 563 - Postponed Evaluation of Annotations
&lt;/h3&gt;

&lt;p&gt;Knowing what's a PEP, let's go back to the code example above.&lt;/p&gt;

&lt;p&gt;If you hadn't seen something like the &lt;code&gt;Optional["Person"]&lt;/code&gt; part before, you might have cringed a bit. I did the first time I discovered that was valid, but it was understandable as it would solve the problem.&lt;/p&gt;

&lt;p&gt;Then &lt;a href="https://twitter.com/llanga"&gt;Łukasz Langa&lt;/a&gt; had a smart idea and wrote &lt;a href="https://www.python.org/dev/peps/pep-0563/"&gt;PEP 563&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If the way type annotations were interpreted changed, and if they were &lt;em&gt;implicitly&lt;/em&gt; understood by Python as if they were all just strings, then we would not have to put all those classes inside strings in strange places in our code.&lt;/p&gt;

&lt;p&gt;So, we would write our code like:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;child&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then whenever &lt;code&gt;python&lt;/code&gt; read our file &lt;code&gt;./main.py&lt;/code&gt; it would see it as if it was written like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"str"&lt;/span&gt;
    &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Optional[Person]"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, &lt;code&gt;python&lt;/code&gt; would run our code happily and without breaking.&lt;/p&gt;

&lt;p&gt;And we, the developers would be much happier not having to remember where to put things inside strings and where not.&lt;/p&gt;

&lt;p&gt;And we would be able to keep using autocompletion and type checks even in these type annotations with forward references. For example, triggering autocompletion inside a string, with the previous technique, might not always work, but with this change that wouldn't be a problem anymore.&lt;/p&gt;

&lt;p&gt;And in the case that some tool ended up using these type annotations at runtime for other reasons, there were still ways to get the information at runtime, with some &lt;em&gt;small caveats&lt;/em&gt;, but it was still possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spoiler Alert&lt;/strong&gt;: These &lt;em&gt;small caveats&lt;/em&gt; are what later would become a cumbersome problem for Pydantic, but we'll get there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Have in mind that this was done several years ago, in fact, the same year Pydantic was released for the first time. Using type annotations at runtime for other purposes than static analysis was not a common use case if at all. It's remarkable that it was even accounted for.&lt;/p&gt;

&lt;p&gt;Now, as this would change the behavior of Python internally in a more or less drastic way, it would not be enforced by default yet. Instead, it was made available using a special import, &lt;code&gt;from __future__ import annotations&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;child&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And as now these type annotations were treated as just strings, it allowed some interesting tricks when using them only for static analysis, like using typing features from future versions of Python in previous versions.&lt;/p&gt;

&lt;p&gt;For example, declaring &lt;code&gt;Person | None&lt;/code&gt; instead of &lt;code&gt;Optional[Person]&lt;/code&gt;, avoiding the extra &lt;code&gt;Optional&lt;/code&gt; and the extra import, even in Python 3.7 (that feature is available in Python 3.10 but not in Python 3.7):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Have in mind that this would only work for static analysis tools, your editor could understand that even in Python 3.7, but Pydantic wouldn't be able to use it and wouldn't work correctly.&lt;/p&gt;

&lt;p&gt;This has been there, available since Python 3.7. And that behavior was planned to be the default for Python 3.10 onwards (not now, but keep reading).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pydantic and PEP 563
&lt;/h3&gt;

&lt;p&gt;Now, forward to the present, a couple of months ago.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pydantic-docs.helpmanual.io/usage/postponed_annotations/"&gt;Pydantic already has &lt;em&gt;some&lt;/em&gt; support&lt;/a&gt; for using &lt;code&gt;from __future__ import annotations&lt;/code&gt; in the code as made possible by PEP 563. And in many cases, it works fine. For example, this works:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;


&lt;span class="c1"&gt;# ✅ Pydantic models outside of functions will always work
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&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;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/items/"&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;create_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&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;item&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there are &lt;em&gt;some caveats&lt;/em&gt; that wouldn't work. For example, this doesn't work:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# 🚨 Pydantic models INSIDE of functions would not work
&lt;/span&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
        &lt;span class="n"&gt;description&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;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;


    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/items/"&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;create_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&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;item&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run that code, you would get a disconcerting error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NameError: name 'Item' is not defined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To solve it in this case, you could move the &lt;code&gt;Item&lt;/code&gt; class outside of the function. And there are some other similar corner cases.&lt;/p&gt;

&lt;p&gt;These types of disconcerting problems would be especially inconvenient for newcomers to Python (and probably to many experienced Python developers as well), as the problem is not obvious at all for someone that doesn't know the internals (it wasn't obvious to me, and I built FastAPI and Typer 😅).&lt;/p&gt;

&lt;p&gt;Python is an example of a very inclusive global tech community, welcoming newcomers from all around the world, from many disciplines. It is being used to solve the most complex problems, including taking pictures of black holes, running drones on Mars, and building the most sophisticated artificial intelligence systems. But at the same time, it's many people's first programing language for its ease of use and its simplicity. And many Python developers don't even consider themselves "developers", even while they use it to solve problems.&lt;/p&gt;

&lt;p&gt;So, having an inconvenience like this by default would not be ideal. There are other caveats but I don't want to go deeper into the technical details than I already have. You can read more about them on the &lt;a href="https://github.com/samuelcolvin/pydantic/issues/2678"&gt;Pydantic issue&lt;/a&gt;, the &lt;a href="https://mail.python.org/archives/list/python-dev@python.org/thread/QSASX6PZ3LIIFIANHQQFS752BJYFUFPY/#UITB2A657TAINAGWGRD6GCKWFC5PEBIZ"&gt;mailing list thread&lt;/a&gt;, and &lt;a href="https://mail.python.org/archives/list/python-dev@python.org/thread/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/"&gt;Łukasz's detailed explanation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  PEP 649 - Deferred Evaluation Of Annotations Using Descriptors
&lt;/h3&gt;

&lt;p&gt;Recently, Larry Hastings that had been working on an alternative to PEP 563, &lt;a href="https://www.python.org/dev/peps/pep-0649/"&gt;PEP 649&lt;/a&gt;, contacted Samuel Colvin (Pydantic's author) and me (author of FastAPI and Typer), as suggested by &lt;a href="https://twitter.com/brettsky"&gt;Brett Cannon&lt;/a&gt; (from the Python Steering Council), to see if and how those changes would affect us.&lt;/p&gt;

&lt;p&gt;We realized that the changes from PEP 563 (the other one) would be permanently added to Python 3.10 (not requiring the &lt;code&gt;from __future__ import annotations&lt;/code&gt;), and the caveats and problems still didn't have a solution.&lt;/p&gt;

&lt;p&gt;Suddenly it was also clear that these use cases of using type annotations at runtime instead of only for static analysis were not an obvious use case for everyone involved, including the same Larry Hastings who was working on what would be a potential solution for these use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asking for Reconsideration
&lt;/h3&gt;

&lt;p&gt;Sadly, we realized all this very late, only weeks before these changes would be set in stone in Python 3.10 (in the end they weren't). Nevertheless, we showed our concerns.&lt;/p&gt;

&lt;p&gt;If you read about all this before, that's probably why. It was shared a lot, and it got a bit out of hand.&lt;/p&gt;

&lt;p&gt;And sadly, there were some radical comments attacking several of the parts involved (the Python Steering Council, us, etc), as if it was a fight between different groups. 😕&lt;/p&gt;

&lt;p&gt;In reality, we are just one big group, the Python Community, and we are all trying to do the best for all of us.&lt;/p&gt;

&lt;p&gt;Sadly, all this sudden friction brought a lot of increased stress to all the parties involved. To the Python Steering Council, Core Python Developers, and us, library authors.&lt;/p&gt;

&lt;p&gt;Fortunately, everything came out well in the end.&lt;/p&gt;

&lt;p&gt;Here's a big shoutout to &lt;a href="https://twitter.com/willingcarol"&gt;Carol Willing&lt;/a&gt; that, despite the added stress generated for her and everyone else involved, she helped a lot reconciling different points of view, reducing the friction, and calming down all the situation. That capacity of acknowledging and adopting other's points of view is priceless. We need more Carol Willings in the world. 🤓&lt;/p&gt;

&lt;h3&gt;
  
  
  Python Steering Council decision
&lt;/h3&gt;

&lt;p&gt;In case you didn't know, the decision of what goes into Python and what doesn't is done by the &lt;strong&gt;Python Steering Council&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is currently formed by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Barry Warsaw&lt;/li&gt;
&lt;li&gt;Brett Cannon&lt;/li&gt;
&lt;li&gt;Carol Willing&lt;/li&gt;
&lt;li&gt;Pablo Galindo Salgado&lt;/li&gt;
&lt;li&gt;Thomas Wouters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, back to the story, after a couple of days of that previous discussion, during the next Python Steering Council meeting, they &lt;a href="https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/"&gt;unanimously decided to roll back the decision&lt;/a&gt; of making these type annotations as strings (as described in PEP 563) being the default behavior.&lt;/p&gt;

&lt;p&gt;Having those string type annotations by default in Python 3.10 had been decided some time ago, and rolling that change back only weeks before the "feature freeze" (the moment where no more changes are accepted into the next version) was a big decision, involving a lot of extra stress and effort.&lt;/p&gt;

&lt;p&gt;Nevertheless, they took the decision in order to support the community of users of FastAPI, Pydantic, and other libraries using these features:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We can’t risk breaking even a small subset of the FastAPI/pydantic users, not to mention other uses of evaluated type annotations that we’re not aware of yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This, again, shows the strong commitment of the Python community, starting from the Steering Council, to be inclusive, and supportive of all users, with different use cases.&lt;/p&gt;

&lt;p&gt;Here's another big shoutout to &lt;a href="https://twitter.com/pyblogsal"&gt;Pablo Galindo&lt;/a&gt;, who &lt;a href="https://github.com/samuelcolvin/pydantic/issues/2678#issuecomment-823569522"&gt;took all the extra work&lt;/a&gt; to perform all the last-minute changes, and even voted in favor of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;The decision was to keep the current behavior, of allowing &lt;code&gt;from __future__ import annotations&lt;/code&gt; in the code, as defined by PEP 563, but not as the default behavior.&lt;/p&gt;

&lt;p&gt;This will provide enough time to find a solution or an alternative that works for all the use cases, including Pydantic, FastAPI, and also the use cases that are interested exclusively in static analysis.&lt;/p&gt;

&lt;p&gt;This is the best possible outcome for everyone. 🎉&lt;/p&gt;

&lt;p&gt;It gives enough time to find an alternate solution and it avoids hurried decisions with little time that could have unknown negative effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who cares about FastAPI and Pydantic
&lt;/h2&gt;

&lt;p&gt;Now, in general, how does the future of FastAPI and Pydantic look like? Who cares about them?&lt;/p&gt;

&lt;p&gt;FastAPI, using Pydantic, was included for the first time in the last Python Developer Survey, and despite being the first year in it, it was already &lt;a href="https://www.jetbrains.com/lp/python-developers-survey-2020/#FrameworksLibraries"&gt;ranked as the third most popular web framework&lt;/a&gt;, after Flask and Django. This shows that it's being useful for many people.&lt;/p&gt;

&lt;p&gt;It was also included in the latest &lt;a href="https://www.thoughtworks.com/radar/languages-and-frameworks?blipid=202104087"&gt;ThoughtWorks Technology Radar&lt;/a&gt;, as one of the technologies that enterprises should start trying out.&lt;/p&gt;

&lt;p&gt;FastAPI and Pydantic are currently being used by many products and organizations, from the biggest ones you've heard of, to the smallest teams, including solo developers.&lt;/p&gt;

&lt;p&gt;Several popular and widely used cloud providers, SaaS tools, databases, etc. are adding documentation, tutorials, and even improving their offers to better serve the FastAPI users.&lt;/p&gt;

&lt;p&gt;The most popular code editors for Python, &lt;a href="https://www.jetbrains.com/pycharm/"&gt;PyCharm&lt;/a&gt; and &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;, have been working on improving their support for FastAPI and Pydantic. I have even talked to both teams directly. 🤓&lt;/p&gt;

&lt;p&gt;This is particularly interesting because FastAPI was designed to have the best support from editors, to provide the best developer experience possible. FastAPI and Pydantic use almost exclusively standard features of the language. When editors improve their support (even more) for these tools, they are actually improving their support for the features of the language itself. And this benefits many other use cases apart from FastAPI and Pydantic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Python is a great community.&lt;/p&gt;

&lt;p&gt;We are all trying to make it better for all of us, from the Steering Council and Core Developers to library authors and even &lt;a href="https://fastapi.tiangolo.com/fastapi-people/"&gt;those who help others&lt;/a&gt; using these libraries.&lt;/p&gt;

&lt;p&gt;FastAPI and Pydantic are part of this community that includes and supports everyone, with all their use cases.&lt;/p&gt;

&lt;p&gt;And that's the main reason why the future of FastAPI and Pydantic is so bright. Because the future of Python is bright. We all make this future. ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thanks to everyone involved in finding a solution and improving the Python community. 🙇&lt;/p&gt;

&lt;p&gt;And special thanks to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/llanga"&gt;Łukasz Langa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/willingcarol"&gt;Carol Willing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/samuel_colvin"&gt;Samuel Colvin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;for their review and feedback on this article before publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hey! 👋 I'm Sebastián Ramírez (&lt;a href="https://tiangolo.com"&gt;tiangolo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow me, contact me, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;LinkedIn: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev: tiangolo.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/"&gt;Medium: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.com"&gt;Web: tiangolo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>pydantic</category>
    </item>
    <item>
      <title>Deploying FastAPI (and other) apps with HTTPS powered by Traefik</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Sat, 06 Mar 2021 19:13:19 +0000</pubDate>
      <link>https://dev.to/tiangolo/deploying-fastapi-and-other-apps-with-https-powered-by-traefik-5dik</link>
      <guid>https://dev.to/tiangolo/deploying-fastapi-and-other-apps-with-https-powered-by-traefik-5dik</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/deploying-fastapi-and-other-apps-with-https-powered-by-traefik-5dik"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/deploying-fastapi-and-other-apps-with-https-powered-by-traefik-e30b5058b98d"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/deploying-fastapi-apps-with-https-powered-by-traefik/README.md"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Let's say you have a &lt;strong&gt;FastAPI&lt;/strong&gt; application... or actually, any other type of web application, including a &lt;a href="https://panel.holoviz.org/"&gt;Panel&lt;/a&gt; dashboard with Pandas DataFrames and Bokeh visualizations, or a &lt;a href="https://www.streamlit.io/"&gt;Streamlit&lt;/a&gt; application. These are, in the end, web applications. You could think of many other examples.&lt;/p&gt;

&lt;p&gt;Now let's say it all works well locally, on your machine.&lt;/p&gt;

&lt;p&gt;But in most cases, the purpose of these web apps is to be available on the real web (not only on your machine), so that others can actually access them.&lt;/p&gt;

&lt;p&gt;So you need to "deploy" them somewhere, on a remote server.&lt;/p&gt;

&lt;p&gt;And then you would want to have secure communication between your app clients (web browsers, mobile apps, etc.) and your server web application.&lt;/p&gt;

&lt;p&gt;So, you should have &lt;strong&gt;HTTPS&lt;/strong&gt;. 🔒&lt;/p&gt;

&lt;p&gt;But although it might sound like a simple "option" to enable, it's quite more complex than that... and &lt;a href="https://doc.traefik.io/traefik/"&gt;Traefik&lt;/a&gt; can help you a lot.&lt;/p&gt;




&lt;p&gt;I have been a long-time fan of Traefik, even before creating FastAPI.&lt;/p&gt;

&lt;p&gt;And recently I had the chance to make an event/webinar with them. 🎉&lt;/p&gt;

&lt;p&gt;You can watch the recording of the &lt;a href="https://traefik.io/resources/traefik-fastapi-kuberrnetes-ai-ml/?utm_campaign=Influencer:%20Sebastian%20Ramirez,%20FastAPI%20&amp;amp;utm_content=155438367&amp;amp;utm_medium=social&amp;amp;utm_source=twitter&amp;amp;hss_channel=tw-4890312130"&gt;video here on the Traefik resources' website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About HTTPS
&lt;/h2&gt;

&lt;p&gt;HTTPS is quite more complex than "enabling an option".&lt;/p&gt;

&lt;p&gt;The protocol any of your applications will need to "talk" is actually the same HTTP, so you don't have to change anything in your web apps to change from HTTP to HTTPS.&lt;/p&gt;

&lt;p&gt;But that HTTP communication has to go through a secure connection (TLS/SSL), that's where the "S" in HTTPS comes from, "HTTP Secure".&lt;/p&gt;

&lt;p&gt;There's a whole process required, including acquiring HTTPS (TLS/SSL) certificates. But fortunately, &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; provides them for free... you just have to set everything up.&lt;/p&gt;

&lt;p&gt;But then, "setting everything up" including acquiring the certificates, installing them where appropriate, renewing them every three months, etc. It's all a relatively complex process. But Traefik can do all that for you.&lt;/p&gt;

&lt;p&gt;To quickly learn how HTTPS works from the consumer's perspective, I highly encourage you to go and check &lt;a href="https://howhttps.works/"&gt;HowHTTPS.works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then you can go and read the short summary of what you need to know as a &lt;em&gt;developer&lt;/em&gt; in the &lt;a href="https://fastapi.tiangolo.com/deployment/https/"&gt;FastAPI docs: About HTTPS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain name
&lt;/h2&gt;

&lt;p&gt;HTTPS is tied to a domain name because the TLS certificate is for that specific domain name.&lt;/p&gt;

&lt;p&gt;So, you need to have one or buy one.&lt;/p&gt;

&lt;p&gt;I buy my domains at &lt;a title="This link has my referral code, if you don't want to use it, you can also type it in the browser. 🤷" href="https://www.name.com/referral/16bb2"&gt;Name.com&lt;/a&gt;, it's quite cheap and it has worked quite well for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote server
&lt;/h2&gt;

&lt;p&gt;You will also need a "cloud" or remote server.&lt;/p&gt;

&lt;p&gt;It's frequently called a "VPS" for "virtual private server". It's a "private server" because you get a full Linux system with full control of it (contrary to a "shared hosting"). And it's "virtual" because what providers do is create a virtual machine and make it available for you, instead of installing a real physical server, that's why they are affordable.&lt;/p&gt;

&lt;p&gt;For simplicity, I would suggest these providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a title="This link has my referral code, if you don't want to use it, you can also type it in the browser. 🤷" href="https://m.do.co/c/fc6c7539f7a9"&gt;DigitalOcean&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="This link has my referral code, if you don't want to use it, you can also type it in the browser. 🤷" href="https://www.linode.com/?r=8ee6f60b2ddb258bba3fefe264771bca3660fb97"&gt;Linode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="This link has my referral code, if you don't want to use it, you can also type it in the browser. 🤷" href="https://www.vultr.com/?ref=7529603"&gt;Vultr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I personally have things in each one of those. They all work great, they have a simple and nice user experience, and are quite cheap.&lt;/p&gt;

&lt;p&gt;Even $5 or $10 USD a month is enough to have one of the small servers up and running.&lt;/p&gt;

&lt;p&gt;You can also go and use one of the giant cloud infrastructure providers if you want, learn all their terminology and components, set up all the accounts, permissions, etc. And then use them. But for this example, I would suggest one of the three above as it will be a lot simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS records
&lt;/h2&gt;

&lt;p&gt;When you create a remote server, it will have a public IP.&lt;/p&gt;

&lt;p&gt;But now you need to configure your domain to point to that IP, so that when your users go to your domain, they end up talking to your remote server in its IP.&lt;/p&gt;

&lt;p&gt;There's a set of "records" that do that, they are called "DNS records" (DNS for "Domain Name System").&lt;/p&gt;

&lt;p&gt;Those records are stored in "Name Servers". All of these cloud providers above have free Name Servers, so you can use them to store that information about pointing domains to IPs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: those same DNS records are also used for configuring email, and other related small things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: all these Name Server and DNS changes are automatically copied and replicated through the web so that everyone in the world knows where to access the information about your domain, and then, with that, they will know to which IP they should talk to when interacting with your domain. Because that replication takes some time, after you save some of these changes, they can take from minutes to hours to be ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Name Servers
&lt;/h3&gt;

&lt;p&gt;The first step is in your "registrar" (the entity that sold you the domain, e.g. Name.com). In there, you define what are the Name Servers for your domain.&lt;/p&gt;

&lt;p&gt;You will probably first want to remove the default Name Servers. After buying a domain, the default Name Servers are normally the ones for the same registrar (e.g. Name.com), and normally all they do is have &lt;strong&gt;DNS records&lt;/strong&gt; to point the domain to a placeholder page full of ads, but they normally don't allow you to create &lt;strong&gt;DNS records&lt;/strong&gt; (like pointing the domain to an IP address).&lt;/p&gt;

&lt;p&gt;So, you will probably want to remove those default &lt;strong&gt;Name Servers&lt;/strong&gt; and add the ones for your VPS provider.&lt;/p&gt;

&lt;p&gt;E.g. you could add the Name Servers for DigitalOcean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DNS Records
&lt;/h3&gt;

&lt;p&gt;After you configure the &lt;strong&gt;Name Servers&lt;/strong&gt; for your domain to be the ones for your cloud provider, you can now go to that cloud provider and set up the &lt;strong&gt;DNS Records&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Depending on your cloud provider, they will have some section to configure "domains", "domain zones", or "networks", in the end, they all refer to the same configurations for &lt;strong&gt;DNS records&lt;/strong&gt; for a specific domain.&lt;/p&gt;

&lt;p&gt;So, the next step is to create a configuration there for your specific domain (sometimes called a "domain zone").&lt;/p&gt;

&lt;p&gt;Then, inside of that domain configuration, you need to add a &lt;strong&gt;DNS record&lt;/strong&gt; to point any web communication to your cloud server.&lt;/p&gt;

&lt;p&gt;There are several types of DNS records, the one we need is an &lt;strong&gt;A record&lt;/strong&gt;, when you are creating a DNS record, those are normally the default type as they are the most important one.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;A record&lt;/strong&gt; has an &lt;strong&gt;IP&lt;/strong&gt; and a &lt;strong&gt;hostname&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;IP&lt;/strong&gt; would be the one for your remote server. You might need to go to the section in the dashboard where your server is located to copy that IP.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;hostname&lt;/strong&gt; would be your domain or any sub-domain. So, if you bought &lt;code&gt;example.com&lt;/code&gt;, you can set the record to &lt;code&gt;example.com&lt;/code&gt;, or to &lt;code&gt;somesubdomain.example.com&lt;/code&gt; or also &lt;code&gt;a.long.sub.domain.example.com&lt;/code&gt;. In most cases, you can even use &lt;code&gt;*.example.com&lt;/code&gt;, which will match any sub-domain and point it to the IP you specify.&lt;/p&gt;

&lt;p&gt;You can create multiple &lt;strong&gt;A records&lt;/strong&gt;, one for each domain or sub-domain. And each of them can point to different IPs. That's also why you see some applications that use several domains, like &lt;code&gt;dashboard.example.com&lt;/code&gt; and &lt;code&gt;api.example.com&lt;/code&gt;, to handle different parts of the same system in different servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: depending on the provider, you might need to use the symbol &lt;code&gt;@&lt;/code&gt; in the hostname to mean "the same domain I'm configuring", so, for the domain configurations for &lt;code&gt;example.com&lt;/code&gt;, creating an &lt;strong&gt;A record&lt;/strong&gt; with some IP and the hostname &lt;code&gt;@&lt;/code&gt; would mean "point the same domain &lt;code&gt;example.com&lt;/code&gt; to that IP address".&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait
&lt;/h3&gt;

&lt;p&gt;You might have to wait sometime for these DNS changes to replicate.&lt;/p&gt;

&lt;p&gt;You can test if your computer already has access to the most recent version of your records with the tool &lt;code&gt;ping&lt;/code&gt; from the command line. For example, checking for the domain &lt;code&gt;tiangolo.com&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ping tiangolo.com                               
&lt;span class="go"&gt;PING tiangolo.com (104.198.14.52) 56(84) bytes of data.
64 bytes from 52.14.198.104.bc.googleusercontent.com (104.198.14.52): icmp_seq=1 ttl=103 time=204 ms
64 bytes from 52.14.198.104.bc.googleusercontent.com (104.198.14.52): icmp_seq=2 ttl=103 time=226 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can see the IP address is &lt;code&gt;104.198.14.52&lt;/code&gt;. If that's what you just configured, congrats!&lt;/p&gt;

&lt;p&gt;The DNS records are ready. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the video
&lt;/h2&gt;

&lt;p&gt;From this point, you should be able to &lt;a href="https://traefik.io/resources/traefik-fastapi-kuberrnetes-ai-ml/?utm_campaign=Influencer:%20Sebastian%20Ramirez,%20FastAPI%20&amp;amp;utm_content=155438367&amp;amp;utm_medium=social&amp;amp;utm_source=twitter&amp;amp;hss_channel=tw-4890312130"&gt;follow the video recording&lt;/a&gt; with all the explanations.&lt;/p&gt;

&lt;p&gt;So I'll keep the rest of this post as simple as possible, mainly showing you the config files so you can copy all the examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple FastAPI app
&lt;/h2&gt;

&lt;p&gt;Let's start with a basic &lt;a href="https://fastapi.tiangolo.com/"&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;&lt;/a&gt; app.&lt;/p&gt;

&lt;p&gt;I'm assuming that you know a bit about FastAPI, if you don't, feel free to check the documentation, it is written as a tutorial.&lt;/p&gt;

&lt;p&gt;If you want to see the explanation step by step, feel free to check the video.&lt;/p&gt;

&lt;p&gt;The basic app we will use is in a file at &lt;code&gt;./app/main.py&lt;/code&gt;, with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&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;read_main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello World of FastAPI with Traefik"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dockerfile
&lt;/h2&gt;

&lt;p&gt;We will use &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; to deploy everything.&lt;/p&gt;

&lt;p&gt;So, make sure you install it.&lt;/p&gt;

&lt;p&gt;Then we need a file at &lt;code&gt;./app/Dockerfile&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; tiangolo/uvicorn-gunicorn-fastapi:python3.8&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./app /app/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we are using the official FastAPI Docker image: &lt;code&gt;tiangolo/uvicorn-gunicorn-fastapi:python3.8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The official base Docker image does most of the work for us, so we just have to copy the code inside.&lt;/p&gt;

&lt;p&gt;Make sure you have &lt;a href="https://docs.docker.com/get-docker/"&gt;Docker installed&lt;/a&gt; on your local computer and in the remote server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare your cloud server
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Connect to your remote server from your terminal with SSH, it could be something like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh root@fastapi-with-traefik.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update the list of package versions available:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Upgrade the packages to the latest version:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker Compose
&lt;/h2&gt;

&lt;p&gt;We are using Docker Compose to manage all the configurations. So make you &lt;a href="https://docs.docker.com/compose/install/"&gt;install Docker Compose&lt;/a&gt; locally and on the remote server.&lt;/p&gt;

&lt;p&gt;To prevent Docker Compose from hanging, install &lt;code&gt;haveged&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;apt install haveged
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;: Docker Compose uses the internal pseudo-random number generators of the machine. But in a freshly installed/created cloud server, it might not have enough of that "randomness". And that could make the Docker Compose commands hang forever waiting for enough "randomness" to use. &lt;code&gt;haveged&lt;/code&gt; prevents/fixes that issue.&lt;/p&gt;

&lt;p&gt;After that, you can check that Docker Compose works correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Compose files
&lt;/h2&gt;

&lt;p&gt;For all the detailed explanations of the Docker Compose files, &lt;a href="https://traefik.io/resources/traefik-fastapi-kuberrnetes-ai-ml/?utm_campaign=Influencer:%20Sebastian%20Ramirez,%20FastAPI%20&amp;amp;utm_content=155438367&amp;amp;utm_medium=social&amp;amp;utm_source=twitter&amp;amp;hss_channel=tw-4890312130"&gt;check the video recording&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure you update the domains from &lt;code&gt;example.com&lt;/code&gt; to use yours, and the email to register with Let's Encrypt, you will receive notifications about your expiring certificates in that email.&lt;/p&gt;

&lt;p&gt;Also, make sure you add the right &lt;strong&gt;DNS records&lt;/strong&gt; for your main application, and for the Traefik dashboard, and update them in the Docker Compose files accordingly.&lt;/p&gt;

&lt;p&gt;Here are the Docker Compose files if you want to easily copy them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker-compose.traefik.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Use the latest v2.3.x Traefik image available&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v2.3&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Listen on port 80, default for HTTP, necessary to redirect to HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:80&lt;/span&gt;
      &lt;span class="c1"&gt;# Listen on port 443, default for HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;443:443&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik for this service, to make it available in the public network&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.enable=true&lt;/span&gt;
      &lt;span class="c1"&gt;# Define the port inside of the Docker service to use&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080&lt;/span&gt;
      &lt;span class="c1"&gt;# Make Traefik use this domain in HTTP&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-http.entrypoints=http&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-http.rule=Host(`traefik.fastapi-with-traefik.example.com`)&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the traefik-public network (declared below)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.docker.network=traefik-public&lt;/span&gt;
      &lt;span class="c1"&gt;# traefik-https the actual router using HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-https.entrypoints=https&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-https.rule=Host(`traefik.fastapi-with-traefik.example.com`)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-https.tls=true&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the "le" (Let's Encrypt) resolver created below&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-https.tls.certresolver=le&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the special Traefik service api@internal with the web UI/Dashboard&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-https.service=api@internal&lt;/span&gt;
      &lt;span class="c1"&gt;# https-redirect middleware to redirect HTTP to HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.middlewares.https-redirect.redirectscheme.scheme=https&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.middlewares.https-redirect.redirectscheme.permanent=true&lt;/span&gt;
      &lt;span class="c1"&gt;# traefik-http set up only to use the middleware to redirect to https&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-http.middlewares=https-redirect&lt;/span&gt;
      &lt;span class="c1"&gt;# admin-auth middleware with HTTP Basic auth&lt;/span&gt;
      &lt;span class="c1"&gt;# Using the environment variables USERNAME and HASHED_PASSWORD&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.middlewares.admin-auth.basicauth.users=${USERNAME?Variable not set}:${HASHED_PASSWORD?Variable not set}&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable HTTP Basic auth, using the middleware created above&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.traefik-dashboard-https.middlewares=admin-auth&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Add Docker as a mounted volume, so that Traefik can read the labels of other services&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock:ro&lt;/span&gt;
      &lt;span class="c1"&gt;# Mount the volume to store the certificates&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik-public-certificates:/certificates&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Docker in Traefik, so that it reads labels from Docker services&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.docker&lt;/span&gt;
      &lt;span class="c1"&gt;# Do not expose all Docker services, only the ones explicitly exposed&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.docker.exposedbydefault=false&lt;/span&gt;
      &lt;span class="c1"&gt;# Create an entrypoint "http" listening on port 80&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--entrypoints.http.address=:80&lt;/span&gt;
      &lt;span class="c1"&gt;# Create an entrypoint "https" listening on port 443&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--entrypoints.https.address=:443&lt;/span&gt;
      &lt;span class="c1"&gt;# Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.le.acme.email=admin@example.com&lt;/span&gt;
      &lt;span class="c1"&gt;# Store the Let's Encrypt certificates in the mounted volume&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.le.acme.storage=/certificates/acme.json&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the TLS Challenge for Let's Encrypt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.le.acme.tlschallenge=true&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable the access log, with HTTP requests&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--accesslog&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable the Traefik log, for configurations and errors&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--log&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable the Dashboard and API&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--api&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the public network created to be shared between Traefik and&lt;/span&gt;
      &lt;span class="c1"&gt;# any other service that needs to be publicly available with HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik-public&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Create a volume to store the certificates, there is a constraint to make sure&lt;/span&gt;
  &lt;span class="c1"&gt;# Traefik is always deployed to the same Docker node with the same volume containing&lt;/span&gt;
  &lt;span class="c1"&gt;# the HTTPS certificates&lt;/span&gt;
  &lt;span class="na"&gt;traefik-public-certificates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Use the previously created public network "traefik-public", shared with other&lt;/span&gt;
  &lt;span class="c1"&gt;# services that need to be publicly available via this Traefik&lt;/span&gt;
  &lt;span class="na"&gt;traefik-public&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker-compose.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Enable Traefik for this specific "backend" service&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.enable=true&lt;/span&gt;
      &lt;span class="c1"&gt;# Define the port inside of the Docker service to use&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.services.app.loadbalancer.server.port=80&lt;/span&gt;
      &lt;span class="c1"&gt;# Make Traefik use this domain in HTTP&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-http.entrypoints=http&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-http.rule=Host(`fastapi-with-traefik.example.com`)&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the traefik-public network (declared below)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.docker.network=traefik-public&lt;/span&gt;
      &lt;span class="c1"&gt;# Make Traefik use this domain in HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-https.entrypoints=https&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-https.rule=Host(`fastapi-with-traefik.example.com`)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-https.tls=true&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the "le" (Let's Encrypt) resolver&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-https.tls.certresolver=le&lt;/span&gt;
      &lt;span class="c1"&gt;# https-redirect middleware to redirect HTTP to HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.middlewares.https-redirect.redirectscheme.scheme=https&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.middlewares.https-redirect.redirectscheme.permanent=true&lt;/span&gt;
      &lt;span class="c1"&gt;# Middleware to redirect HTTP to HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-http.middlewares=https-redirect&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.app-https.middlewares=admin-auth&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the public network created to be shared between Traefik and&lt;/span&gt;
      &lt;span class="c1"&gt;# any other service that needs to be publicly available with HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik-public&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik-public&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker-compose.override.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:80&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik-public&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start the stacks
&lt;/h2&gt;

&lt;p&gt;There are many approaches for putting your code and Docker images on your server.&lt;/p&gt;

&lt;p&gt;You could have a very sophisticated Continuous Integration system. But for this example using a simple &lt;code&gt;rsync&lt;/code&gt; would be enough.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsync -a ./* root@fastapi-with-traefik.example.com:/root/code/fastapi-with-traefik/ 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inside of your server, make sure you create the Docker network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;docker network create traefik-public
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create the environment variables for HTTP Basic Auth.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the username, e.g.:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create an environment variable with the password, e.g.:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changethis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;openssl&lt;/code&gt; to generate the "hashed" version of the password and store it in an environment variable:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HASHED_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl passwd &lt;span class="nt"&gt;-apr1&lt;/span&gt; &lt;span class="nv"&gt;$PASSWORD&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can start the &lt;strong&gt;Traefik&lt;/strong&gt; Docker Compose stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;docker-compose -f docker-compose.traefik.yml up
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, start the main Docker Compose stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;docker-compose -f docker-compose.yml up -d
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Check your app
&lt;/h2&gt;

&lt;p&gt;After that, if everything worked correctly (and probably it didn't work correctly the first time 😅), you should be able to check your new application live at your domain, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://fastapi-with-traefik.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Traefik dashboard at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://traefik.fastapi-with-traefik.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Traefik dashboard would be protected by HTTP Basic Auth, so no one can go and tamper with your Traefik.&lt;/p&gt;

&lt;h2&gt;
  
  
  Celebrate 🎉
&lt;/h2&gt;

&lt;p&gt;Congrats! That's a very stable way to have a production application deployed.&lt;/p&gt;

&lt;p&gt;You can probably improve that a lot, add Continuous Integration, monitoring, logging, use a complete cluster of machines instead of a single one (e.g. use Kubernetes instead of Docker Compose), etc. There's no limit to adding more stuff and improving it all...&lt;/p&gt;

&lt;p&gt;But with this, you already have the minimum to serve your users a secure application.&lt;/p&gt;

&lt;p&gt;And as your deployment is based on Docker, and can be replicated easily and quickly, you could destroy that server, create a new one from scratch, and be live again in minutes. Because it doesn't depend on &lt;em&gt;that&lt;/em&gt;  specific server.&lt;/p&gt;

&lt;p&gt;All the important configurations and setup are in your Docker Compose files.&lt;/p&gt;

&lt;p&gt;And all the important logic and setup of the actual app are in the Docker image (with the &lt;code&gt;Dockerfile&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And Docker itself is taking care of having your application running, restarting it after failures or reboots, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dessert 🍰
&lt;/h2&gt;

&lt;p&gt;Do you want a bit more?&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://github.com/tiangolo/blog-posts/blob/master/deploying-fastapi-apps-with-https-powered-by-traefik/"&gt;the source code for this blog post&lt;/a&gt;, including the latest version of the app and config files, including a basic example with Panel, and one with Streamlit. ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;Here are some extra resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://howhttps.works/"&gt;HowHTTPS.works&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/deployment/https/"&gt;FastAPI docs: HTTPS for developers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Event &lt;a href="https://traefik.io/resources/traefik-fastapi-kuberrnetes-ai-ml/?utm_campaign=Influencer:%20Sebastian%20Ramirez,%20FastAPI%20&amp;amp;utm_content=155438367&amp;amp;utm_medium=social&amp;amp;utm_source=twitter&amp;amp;hss_channel=tw-4890312130"&gt;video recording in &lt;strong&gt;Traefik resources&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/deploying-fastapi-apps-with-https-powered-by-traefik/"&gt;Source code in GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.traefik.io/traefik/"&gt;Traefik docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope that was useful! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hey! 👋 I'm Sebastián Ramírez (&lt;a href="https://tiangolo.com"&gt;tiangolo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow me, contact me, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;LinkedIn: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev: tiangolo.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/"&gt;Medium: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.com"&gt;Web: tiangolo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>https</category>
      <category>fastapi</category>
      <category>traefik</category>
    </item>
    <item>
      <title>FastAPI top-level dependencies</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Sun, 03 Jan 2021 11:16:26 +0000</pubDate>
      <link>https://dev.to/tiangolo/fastapi-top-level-dependencies-8ah</link>
      <guid>https://dev.to/tiangolo/fastapi-top-level-dependencies-8ah</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/fastapi-top-level-dependencies-8ah"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/fastapi-top-level-dependencies-4d083a93a7ac"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/fastapi-top-level-dependencies/README.md"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;FastAPI version &lt;code&gt;0.62.0&lt;/code&gt; comes with global dependencies that you can apply to a whole application.&lt;/p&gt;

&lt;p&gt;As well as top-level dependencies, tags, and other parameters for &lt;code&gt;APIRouter&lt;/code&gt;s, that before were available only on &lt;code&gt;app.include_router()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This makes it easier to put configurations and dependencies (e.g. for authentication) related to a group of &lt;em&gt;path operations&lt;/em&gt; more closely together. 🔒&lt;/p&gt;

&lt;p&gt;Let's start by checking &lt;code&gt;APIRouter&lt;/code&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  Include a router
&lt;/h2&gt;

&lt;p&gt;Imagine you had a file &lt;code&gt;users.py&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/"&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;read_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"rick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"morty"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now let's say you want to include it in the &lt;code&gt;main.py&lt;/code&gt; file with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.dependencies&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_query_token&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_query_token&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, you are applying the tag &lt;code&gt;users&lt;/code&gt; to all the &lt;em&gt;path operations&lt;/em&gt; in &lt;code&gt;users.py&lt;/code&gt;. And you are also applying the dependency &lt;code&gt;get_query_token&lt;/code&gt; to all of them.&lt;/p&gt;

&lt;p&gt;This works, and it was the only/main way to do it up to version 0.62.0.&lt;/p&gt;

&lt;p&gt;But what is not so great about it is that the tag and the dependency are mainly related to &lt;code&gt;users.py&lt;/code&gt;, not to &lt;code&gt;main.py&lt;/code&gt;. But that code had to live in &lt;code&gt;main.py&lt;/code&gt;, instead of being closer to what it is related to.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;APIRouter&lt;/code&gt; top-level dependencies and tags
&lt;/h2&gt;

&lt;p&gt;Now, with FastAPI version &lt;code&gt;0.62.0&lt;/code&gt;, you can declare top-level dependencies, tags, and others in the &lt;code&gt;APIRouter&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;So, the new &lt;code&gt;router.py&lt;/code&gt; can now look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.dependencies&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_query_token&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_query_token&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/"&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;read_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"rick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"morty"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...notice the &lt;code&gt;tags&lt;/code&gt; and &lt;code&gt;dependencies&lt;/code&gt; in the &lt;code&gt;APIRouter&lt;/code&gt;, they can now live closer to their related code! 🎉&lt;/p&gt;

&lt;p&gt;And the &lt;code&gt;main.py&lt;/code&gt; would be simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Global dependencies
&lt;/h2&gt;

&lt;p&gt;In the same way, you can now also declare &lt;code&gt;dependencies&lt;/code&gt; that apply to &lt;strong&gt;all&lt;/strong&gt; the &lt;em&gt;path operations&lt;/em&gt; in the &lt;code&gt;FastAPI&lt;/code&gt; application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.dependencies&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_query_token&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_query_token&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tips
&lt;/h2&gt;

&lt;p&gt;Some tips to adopt a convention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, set all those configs in &lt;code&gt;APIRouter()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Try to &lt;strong&gt;only&lt;/strong&gt; set them in &lt;code&gt;app.include_router()&lt;/code&gt; when you want to override some defaults that can't (or shouldn't) be set in &lt;code&gt;APIRouter&lt;/code&gt; directly.&lt;/li&gt;
&lt;li&gt;Set them in &lt;code&gt;FastAPI()&lt;/code&gt; only when you want them to apply to everything, e.g. some default authentication for a simple app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;You can read more about &lt;a href="https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/"&gt;global dependencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And about &lt;a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/"&gt;&lt;code&gt;APIRouter&lt;/code&gt; top-level &lt;code&gt;dependencies&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt;, and others&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you don't want to miss other news, you can subscribe to the &lt;a href="https://fastapi.tiangolo.com/newsletter/"&gt;&lt;strong&gt;FastAPI and friends&lt;/strong&gt; official newsletter&lt;/a&gt;. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hey! 👋 I'm &lt;a href="https://tiangolo.com"&gt;tiangolo (Sebastián Ramírez)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow me, contact me, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;LinkedIn: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev: tiangolo.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.medium.com/"&gt;Medium: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.com"&gt;Web: tiangolo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>news</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Concurrent Burgers - Understand async / await</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Sun, 17 May 2020 06:57:46 +0000</pubDate>
      <link>https://dev.to/tiangolo/concurrent-burgers-understand-async-await-3n20</link>
      <guid>https://dev.to/tiangolo/concurrent-burgers-understand-async-await-3n20</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/concurrent-burgers-understand-async-await-3n20"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo/concurrent-burgers-understand-async-await-eeec05ae7cfe"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/concurrent-burgers-understand-async-await/README.md"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/async/"&gt;FastAPI's docs&lt;/a&gt; (including translations to other languages)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Modern versions of Python (and other languages) have support for &lt;strong&gt;"asynchronous code"&lt;/strong&gt; using something called &lt;strong&gt;"coroutines"&lt;/strong&gt;, with &lt;strong&gt;&lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;&lt;/strong&gt; syntax.&lt;/p&gt;

&lt;p&gt;Here's a friendly and not very technical explanation to give some intuition about all that, including asynchronous code, concurrency, and parallelism.&lt;/p&gt;

&lt;p&gt;This is taken from the &lt;a href="https://fastapi.tiangolo.com/async/"&gt;docs for FastAPI&lt;/a&gt;, a modern framework for building APIs in Python.&lt;/p&gt;

&lt;p&gt;Although this was written for Python and FastAPI, all the story and information is relevant for other languages that also have &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;, like &lt;strong&gt;JavaScript&lt;/strong&gt; and &lt;strong&gt;Rust&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Now, let's see that phrase by parts in the sections below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous Code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coroutines&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Asynchronous Code
&lt;/h2&gt;

&lt;p&gt;Asynchronous code just means that the language 💬 has a way to tell the computer / program 🤖 that at some point in the code, it 🤖 will have to wait for &lt;em&gt;something else&lt;/em&gt; to finish somewhere else. Let's say that &lt;em&gt;something else&lt;/em&gt; is called "slow-file" 📝.&lt;/p&gt;

&lt;p&gt;So, during that time, the computer can go and do some other work, while "slow-file" 📝 finishes.&lt;/p&gt;

&lt;p&gt;Then the computer / program 🤖 will come back every time it has a chance because it's waiting again, or whenever it 🤖 finished all the work it had at that point. And it 🤖 will see if any of the tasks it was waiting for have already finished, doing whatever it had to do.&lt;/p&gt;

&lt;p&gt;Next, it 🤖 takes the first task to finish (let's say, our "slow-file" 📝) and continues whatever it had to do with it.&lt;/p&gt;

&lt;p&gt;That "wait for something else" normally refers to I/O operations that are relatively "slow" (compared to the speed of the processor and the RAM memory), like waiting for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the data from the client to be sent through the network&lt;/li&gt;
&lt;li&gt;the data sent by your program to be received by the client through the network&lt;/li&gt;
&lt;li&gt;the contents of a file in the disk to be read by the system and given to your program&lt;/li&gt;
&lt;li&gt;the contents your program gave to the system to be written to disk&lt;/li&gt;
&lt;li&gt;a remote API operation&lt;/li&gt;
&lt;li&gt;a database operation to finish&lt;/li&gt;
&lt;li&gt;a database query to return the results&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the execution time is consumed mostly by waiting for I/O operations, they call them "I/O bound" operations.&lt;/p&gt;

&lt;p&gt;It's called "asynchronous" because the computer / program doesn't have to be "synchronized" with the slow task, waiting for the exact moment that the task finishes, while doing nothing, to be able to take the task result and continue the work.&lt;/p&gt;

&lt;p&gt;Instead of that, by being an "asynchronous" system, once finished, the task can wait in line a little bit (some microseconds) for the computer / program to finish whatever it went to do, and then come back to take the results and continue working with them.&lt;/p&gt;

&lt;p&gt;For "synchronous" (contrary to "asynchronous") they commonly also use the term "sequential", because the computer / program follows all the steps in sequence before switching to a different task, even if those steps involve waiting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrency and Burgers
&lt;/h3&gt;

&lt;p&gt;This idea of &lt;strong&gt;asynchronous&lt;/strong&gt; code described above is also sometimes called &lt;strong&gt;"concurrency"&lt;/strong&gt;. It is different from &lt;strong&gt;"parallelism"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt; and &lt;strong&gt;parallelism&lt;/strong&gt; both relate to "different things happening more or less at the same time".&lt;/p&gt;

&lt;p&gt;But the details between &lt;em&gt;concurrency&lt;/em&gt; and &lt;em&gt;parallelism&lt;/em&gt; are quite different.&lt;/p&gt;

&lt;p&gt;To see the difference, imagine the following story about burgers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrent Burgers
&lt;/h3&gt;

&lt;p&gt;You go with your crush 😍 to get fast food 🍔, you stand in line while the cashier 💁 takes the orders from the people in front of you.&lt;/p&gt;

&lt;p&gt;Then it's your turn, you place your order of 2 very fancy burgers 🍔 for your crush 😍 and you.&lt;/p&gt;

&lt;p&gt;You pay 💸.&lt;/p&gt;

&lt;p&gt;The cashier 💁 says something to the guy in the kitchen 👨‍🍳 so he knows he has to prepare your burgers 🍔 (even though he is currently preparing the ones for the previous clients).&lt;/p&gt;

&lt;p&gt;The cashier 💁 gives you the number of your turn.&lt;/p&gt;

&lt;p&gt;While you are waiting, you go with your crush 😍 and pick a table, you sit and talk with your crush 😍 for a long time (as your burgers are very fancy and take some time to prepare ✨🍔✨).&lt;/p&gt;

&lt;p&gt;As you are sitting on the table with your crush 😍, while you wait for the burgers 🍔, you can spend that time admiring how awesome, cute and smart your crush is ✨😍✨.&lt;/p&gt;

&lt;p&gt;While waiting and talking to your crush 😍, from time to time, you check the number displayed on the counter to see if it's your turn already.&lt;/p&gt;

&lt;p&gt;Then at some point, it finally is your turn. You go to the counter, get your burgers 🍔 and come back to the table.&lt;/p&gt;

&lt;p&gt;You and your crush 😍 eat the burgers 🍔 and have a nice time ✨.&lt;/p&gt;




&lt;p&gt;Imagine you are the computer / program 🤖 in that story.&lt;/p&gt;

&lt;p&gt;While you are at the line, you are just idle 😴, waiting for your turn, not doing anything very "productive". But the line is fast because the cashier 💁 is only taking the orders (not preparing them), so that's fine.&lt;/p&gt;

&lt;p&gt;Then, when it's your turn, you do actual "productive" work 🤓, you process the menu, decide what you want, get your crush's 😍 choice, pay 💸, check that you give the correct bill or card, check that you are charged correctly, check that the order has the correct items, etc.&lt;/p&gt;

&lt;p&gt;But then, even though you still don't have your burgers 🍔, your work with the cashier 💁 is "on pause" ⏸, because you have to wait 🕙 for your burgers to be ready.&lt;/p&gt;

&lt;p&gt;But as you go away from the counter and sit on the table with a number for your turn, you can switch 🔀 your attention to your crush 😍, and "work" ⏯ 🤓 on that. Then you are again doing something very "productive" 🤓, as is flirting with your crush 😍.&lt;/p&gt;

&lt;p&gt;Then the cashier 💁 says "I'm finished with doing the burgers" 🍔 by putting your number on the counter's display, but you don't jump like crazy immediately when the displayed number changes to your turn number. You know no one will steal your burgers 🍔 because you have the number of your turn, and they have theirs.&lt;/p&gt;

&lt;p&gt;So you wait for your crush 😍 to finish the story (finish the current work ⏯ / task being processed 🤓), smile gently and say that you are going for the burgers ⏸.&lt;/p&gt;

&lt;p&gt;Then you go to the counter 🔀, to the initial task that is now finished ⏯, pick the burgers 🍔, say thanks and take them to the table. That finishes that step / task of interaction with the counter ⏹. That in turn, creates a new task, of "eating burgers" 🔀 ⏯, but the previous one of "getting burgers" is finished ⏹.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel Burgers
&lt;/h3&gt;

&lt;p&gt;Now let's imagine these aren't "Concurrent Burgers", but "Parallel Burgers".&lt;/p&gt;

&lt;p&gt;You go with your crush 😍 to get parallel fast food 🍔.&lt;/p&gt;

&lt;p&gt;You stand in line while several (let's say 8) cashiers that at the same time are cooks 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳 take the orders from the people in front of you.&lt;/p&gt;

&lt;p&gt;Everyone before you is waiting 🕙 for their burgers 🍔 to be ready before leaving the counter because each of the 8 cashiers goes himself and prepares the burger right away before getting the next order.&lt;/p&gt;

&lt;p&gt;Then it's finally your turn, you place your order of 2 very fancy burgers 🍔 for your crush 😍 and you.&lt;/p&gt;

&lt;p&gt;You pay 💸.&lt;/p&gt;

&lt;p&gt;The cashier goes to the kitchen 👨‍🍳.&lt;/p&gt;

&lt;p&gt;You wait, standing in front of the counter 🕙, so that no one else takes your burgers 🍔 before you do, as there are no numbers for turns.&lt;/p&gt;

&lt;p&gt;As you and your crush 😍 are busy not letting anyone get in front of you and take your burgers whenever they arrive 🕙, you cannot pay attention to your crush 😞.&lt;/p&gt;

&lt;p&gt;This is "synchronous" work, you are "synchronized" with the cashier/cook 👨‍🍳. You have to wait 🕙 and be there at the exact moment that the cashier/cook 👨‍🍳 finishes the burgers 🍔 and gives them to you, or otherwise, someone else might take them.&lt;/p&gt;

&lt;p&gt;Then your cashier/cook 👨‍🍳 finally comes back with your burgers 🍔, after a long time waiting 🕙 there in front of the counter.&lt;/p&gt;

&lt;p&gt;You take your burgers 🍔 and go to the table with your crush 😍.&lt;/p&gt;

&lt;p&gt;You just eat them, and you are done 🍔 ⏹.&lt;/p&gt;

&lt;p&gt;There was not much talk or flirting as most of the time was spent waiting 🕙 in front of the counter 😞.&lt;/p&gt;




&lt;p&gt;In this scenario of the parallel burgers, you are a computer / program 🤖 with two processors (you and your crush 😍), both waiting 🕙 and dedicating their attention ⏯ to be "waiting on the counter" 🕙 for a long time.&lt;/p&gt;

&lt;p&gt;The fast food store has 8 processors (cashiers/cooks) 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳. While the concurrent burgers store might have had only 2 (one cashier and one cook) 💁 👨‍🍳.&lt;/p&gt;

&lt;p&gt;But still, the final experience is not the best 😞.&lt;/p&gt;




&lt;p&gt;This would be the parallel equivalent story for burgers 🍔.&lt;/p&gt;

&lt;p&gt;For a more "real life" example of this, imagine a bank.&lt;/p&gt;

&lt;p&gt;Up to recently, most of the banks had multiple cashiers 👨‍💼👨‍💼👨‍💼👨‍💼 and a big line 🕙🕙🕙🕙🕙🕙🕙🕙.&lt;/p&gt;

&lt;p&gt;All of the cashiers doing all the work with one client after the other 👨‍💼⏯.&lt;/p&gt;

&lt;p&gt;And you have to wait 🕙 in the line for a long time or you lose your turn.&lt;/p&gt;

&lt;p&gt;You probably wouldn't want to take your crush 😍 with you to do errands at the bank 🏦.&lt;/p&gt;

&lt;h3&gt;
  
  
  Burger Conclusion
&lt;/h3&gt;

&lt;p&gt;In this scenario of "fast food burgers with your crush", as there is a lot of waiting 🕙, it makes a lot more sense to have a concurrent system ⏸🔀⏯.&lt;/p&gt;

&lt;p&gt;This is the case for most of the web applications.&lt;/p&gt;

&lt;p&gt;Many, many users, but your server is waiting 🕙 for their not-so-good connection to send their requests.&lt;/p&gt;

&lt;p&gt;And then waiting 🕙 again for the responses to come back.&lt;/p&gt;

&lt;p&gt;This "waiting" 🕙 is measured in microseconds, but still, summing it all, it's a lot of waiting in the end.&lt;/p&gt;

&lt;p&gt;That's why it makes a lot of sense to use asynchronous ⏸🔀⏯ code for web APIs.&lt;/p&gt;

&lt;p&gt;Most of the existing popular Python frameworks (including Flask and Django) were created before the new asynchronous features in Python existed. So, the ways they can be deployed support parallel execution and an older form of asynchronous execution that is not as powerful as the new capabilities.&lt;/p&gt;

&lt;p&gt;Even though the main specification for asynchronous web Python (ASGI) was developed at Django, to add support for WebSockets.&lt;/p&gt;

&lt;p&gt;That kind of asynchronicity is what made NodeJS popular (even though NodeJS is not parallel) and that's the strength of Go as a programing language.&lt;/p&gt;

&lt;p&gt;And that's the same level of performance you get with &lt;strong&gt;FastAPI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And as you can have parallelism and asynchronicity at the same time, you get higher performance than most of the tested NodeJS frameworks and on par with Go, which is a compiled language closer to C &lt;a href="https://www.techempower.com/benchmarks/#section=data-r17&amp;amp;hw=ph&amp;amp;test=query&amp;amp;l=zijmkf-1"&gt;(all thanks to Starlette)&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is concurrency better than parallelism?
&lt;/h3&gt;

&lt;p&gt;Nope! That's not the moral of the story.&lt;/p&gt;

&lt;p&gt;Concurrency is different than parallelism. And it is better on &lt;strong&gt;specific&lt;/strong&gt; scenarios that involve a lot of waiting. Because of that, it generally is a lot better than parallelism for web application development. But not for everything.&lt;/p&gt;

&lt;p&gt;So, to balance that out, imagine the following short story:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You have to clean a big, dirty house.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Yep, that's the whole story&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;There's no waiting 🕙 anywhere, just a lot of work to be done, on multiple places of the house.&lt;/p&gt;

&lt;p&gt;You could have turns as in the burgers example, first the living room, then the kitchen, but as you are not waiting 🕙 for anything, just cleaning and cleaning, the turns wouldn't affect anything.&lt;/p&gt;

&lt;p&gt;It would take the same amount of time to finish with or without turns (concurrency) and you would have done the same amount of work.&lt;/p&gt;

&lt;p&gt;But in this case, if you could bring the 8 ex-cashier/cooks/now-cleaners 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳, and each one of them (plus you) could take a zone of the house to clean it, you could do all the work in &lt;strong&gt;parallel&lt;/strong&gt;, with the extra help, and finish much sooner.&lt;/p&gt;

&lt;p&gt;In this scenario, each one of the cleaners (including you) would be a processor, doing their part of the job.&lt;/p&gt;

&lt;p&gt;And as most of the execution time is taken by actual work (instead of waiting), and the work in a computer is done by a CPU, they call these problems "CPU bound".&lt;/p&gt;




&lt;p&gt;Common examples of CPU bound operations are things that require complex math processing.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audio&lt;/strong&gt; or &lt;strong&gt;image processing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computer vision&lt;/strong&gt;: an image is composed of millions of pixels, each pixel has 3 values / colors, processing that normally requires computing something on those pixels, all at the same time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine Learning&lt;/strong&gt;: it normally requires lots of "matrix" and "vector" multiplications. Think of a huge spreadsheet with numbers and multiplying all of them together at the same time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Learning&lt;/strong&gt;: this is a sub-field of Machine Learning, so, the same applies. It's just that there is not a single spreadsheet of numbers to multiply, but a huge set of them, and in many cases, you use a special processor to build and / or use those models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Concurrency + Parallelism: Web + Machine Learning
&lt;/h3&gt;

&lt;p&gt;With &lt;strong&gt;FastAPI&lt;/strong&gt; you can take the advantage of concurrency that is very common for web development (the same main attractive of NodeJS).&lt;/p&gt;

&lt;p&gt;But you can also exploit the benefits of parallelism and multiprocessing (having multiple processes running in parallel) for &lt;strong&gt;CPU bound&lt;/strong&gt; workloads like those in Machine Learning systems.&lt;/p&gt;

&lt;p&gt;That, plus the simple fact that Python is the main language for &lt;strong&gt;Data Science&lt;/strong&gt;, Machine Learning and especially Deep Learning, make FastAPI a very good match for Data Science / Machine Learning web APIs and applications (among many others).&lt;/p&gt;

&lt;p&gt;To see how to achieve this parallelism in production see the &lt;a href="https://fastapi.tiangolo.com/deployment/"&gt;FastAPI docs for Deployment&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Modern versions of Python have a very intuitive way to define asynchronous code. This makes it look just like normal "sequential" code and do the "awaiting" for you at the right moments.&lt;/p&gt;

&lt;p&gt;When there is an operation that will require waiting before giving the results and has support for these new Python features, you can code it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;burgers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;get_burgers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key here is the &lt;code&gt;await&lt;/code&gt;. It tells Python that it has to wait ⏸ for &lt;code&gt;get_burgers(2)&lt;/code&gt; to finish doing its thing 🕙 before storing the results in &lt;code&gt;burgers&lt;/code&gt;. With that, Python will know that it can go and do something else 🔀 ⏯ in the meanwhile (like receiving another request).&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;await&lt;/code&gt; to work, it has to be inside a function that supports this asynchronicity. To do that, you just declare it with &lt;code&gt;async def&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_burgers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&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="c1"&gt;# Do some asynchronous stuff to create the burgers
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;burgers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...instead of &lt;code&gt;def&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is not asynchronous
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_sequential_burgers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&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="c1"&gt;# Do some sequential stuff to create the burgers
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;burgers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;async def&lt;/code&gt;, Python knows that, inside that function, it has to be aware of &lt;code&gt;await&lt;/code&gt; expressions, and that it can "pause" ⏸ the execution of that function and go do something else 🔀 before coming back.&lt;/p&gt;

&lt;p&gt;When you want to call an &lt;code&gt;async def&lt;/code&gt; function, you have to "await" it. So, this won't work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This won't work, because get_burgers was defined with: async def
&lt;/span&gt;&lt;span class="n"&gt;burgers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_burgers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other forms of asynchronous code
&lt;/h3&gt;

&lt;p&gt;This style of using &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; is relatively new in the language.&lt;/p&gt;

&lt;p&gt;But it makes working with asynchronous code a lot easier.&lt;/p&gt;

&lt;p&gt;This same syntax (or almost identical) was also included recently in modern versions of JavaScript (in Browser and NodeJS).&lt;/p&gt;

&lt;p&gt;But before that, handling asynchronous code was quite more complex and difficult.&lt;/p&gt;

&lt;p&gt;In previous versions of Python, you could have used threads or &lt;a href="http://www.gevent.org/"&gt;Gevent&lt;/a&gt;. But the code is way more complex to understand, debug, and think about.&lt;/p&gt;

&lt;p&gt;In previous versions of NodeJS / Browser JavaScript, you would have used "callbacks". Which leads to &lt;a href="http://callbackhell.com/"&gt;callback hell&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coroutines
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Coroutine&lt;/strong&gt; is just the very fancy term for the thing returned by an &lt;code&gt;async def&lt;/code&gt; function. Python knows that it is something like a function that it can start and that it will end at some point, but that it might be paused ⏸ internally too, whenever there is an &lt;code&gt;await&lt;/code&gt; inside of it.&lt;/p&gt;

&lt;p&gt;But all this functionality of using asynchronous code with &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; is many times summarized as using "coroutines". It is comparable to the main key feature of Go, the "Goroutines".&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Let's see the same phrase from above:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Modern versions of Python have support for &lt;strong&gt;"asynchronous code"&lt;/strong&gt; using something called &lt;strong&gt;"coroutines"&lt;/strong&gt;, with &lt;strong&gt;&lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;&lt;/strong&gt; syntax.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That should make more sense now. ✨&lt;/p&gt;

&lt;p&gt;All that is what powers FastAPI (through Starlette) and what makes it have such an impressive performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;This version has some of the details that are very specific to FastAPI trimmed out. If you want to learn those, including how to get asynchronous performance benefits while still writing standard &lt;code&gt;def&lt;/code&gt; functions and using standard libraries, check the &lt;a href="//ps://fastapi.tiangolo.com/async/"&gt;FastAPI docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want a deeper and much more technical explanation of all this, check this &lt;a href="https://www.youtube.com/watch?v=Xbl7XjFYsN4"&gt;video series from EdgeDB&lt;/a&gt; by &lt;a href="https://twitter.com/llanga"&gt;Łukasz Langa&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hey! 👋 I'm Sebastián Ramírez (&lt;a href="https://tiangolo.com"&gt;tiangolo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow me, contact me, ask questions, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;LinkedIn: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev: tiangolo.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo"&gt;Medium: tiangolo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiangolo.com"&gt;Web: tiangolo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>asynchronous</category>
      <category>concurrency</category>
      <category>python</category>
    </item>
    <item>
      <title>Build a web API from scratch with FastAPI - the workshop</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:08:18 +0000</pubDate>
      <link>https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe</link>
      <guid>https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe</guid>
      <description>&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-866d089d23dc"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/pyconby-web-api-from-scratch-with-fastapi/README.md"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The first FastAPI workshop at PyCon Belarus
&lt;/h2&gt;

&lt;p&gt;Last weekend I had the chance to go to &lt;a href="https://by.pycon.org/"&gt;PyCon Belarus&lt;/a&gt;, I had a great time and met a lot of great people.&lt;/p&gt;

&lt;p&gt;I gave a talk there:&lt;/p&gt;

&lt;p&gt;Liquid error: internal&lt;/p&gt;

&lt;p&gt;And a workshop with about 60 people:&lt;/p&gt;

&lt;p&gt;Liquid error: internal &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the workshop
&lt;/h2&gt;

&lt;p&gt;When creating the workshop I got a bit excited and created too much content for the time I had available.&lt;/p&gt;

&lt;p&gt;The final app ended up having basic OAuth2 authentication, authorization handling with dependencies, tests with full coverage, etc.&lt;/p&gt;

&lt;p&gt;I "gave" a test trial of the full workshop to &lt;a href="https://twitter.com/Mariacamilagl30"&gt;Camila&lt;/a&gt; and the total time was about 9 hours, it wasn't really possible to give it all in 3 hours.&lt;/p&gt;

&lt;p&gt;But as it was made in incremental steps, completing a full new version of the app at every step (or every 2 steps), we could start it and go through it, step by step, and advance as much as possible. And wherever we ended up by the end would still be a valid version of the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first version of the workshop
&lt;/h2&gt;

&lt;p&gt;The speed of a workshop like this has a constant tradeoff, as there's always people that finish some part faster than others, so, at some points some people will be "bored" while others will be stressed finishing some part before the next comes.&lt;/p&gt;

&lt;p&gt;But nevertheless, at the workshop in PyCon Belarus developers were quite fast, and we were able to go up to version 8 of the app, while I was expecting to get only up to about version 5.&lt;/p&gt;

&lt;p&gt;But there were 15 versions. So, for those that wanted to see the final version, here it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code for the final version
&lt;/h2&gt;

&lt;p&gt;I don't have an easy way to provide it step by step with all the explanations here, but if you are curious you can still check here the last version of the code.&lt;/p&gt;

&lt;p&gt;It was all based on the same FastAPI documentation, so, if you want to understand it all you can just follow the full &lt;a href="https://fastapi.tiangolo.com/tutorial/"&gt;FastAPI Tutorial - User Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below are the initial setup instructions and then the link to the full code of the last version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Create a project directory
&lt;/h2&gt;

&lt;p&gt;Create a directory for the project.&lt;/p&gt;

&lt;p&gt;For example &lt;code&gt;./apiapp/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;apiapp
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ./apiapp/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a Python environment
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;./apiapp/&lt;/code&gt; directory, create a new Python environment.&lt;/p&gt;

&lt;p&gt;You could be using Poetry, Pipenv, or other tools.&lt;/p&gt;

&lt;p&gt;To make it simple we are going to use pure Python with the &lt;code&gt;venv&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;Make sure you have a Python version &amp;gt; 3.6:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OR
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3.6 &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OR
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3.7 &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use that Python 3.6+ to create a new environment for your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;python -m venv env
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will create a directory &lt;code&gt;./env/&lt;/code&gt; that will contain a full Python environment, with its own packages, etc.&lt;/p&gt;

&lt;p&gt;And in that environment is that we are going to install packages and everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize git
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ignore that environment in git
&lt;/h3&gt;

&lt;p&gt;Inside of that &lt;code&gt;./env/&lt;/code&gt; directory, create a file &lt;code&gt;.gitignore&lt;/code&gt; with the contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(that's a single &lt;code&gt;*&lt;/code&gt; in the file).&lt;/p&gt;

&lt;p&gt;That will tell git that we want to ignore everything inside that directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activate the environment
&lt;/h3&gt;

&lt;p&gt;Now we need to "activate" the environment.&lt;/p&gt;

&lt;p&gt;This will tell your terminal that when you try to run &lt;code&gt;python&lt;/code&gt; it should use the new Python you just installed in that &lt;code&gt;./env/&lt;/code&gt; directory and not the global one.&lt;/p&gt;

&lt;p&gt;Activate the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure that it worked, check which Python binary is used by your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should show the path of the new Python, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/user/code/apiapp/env/bin/python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Activate in Windows
&lt;/h4&gt;

&lt;p&gt;If you are in Windows, in Git Bash, activate with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./env/Scripts/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using PowerShell, activate with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="se"&gt;\e&lt;/span&gt;nv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\A&lt;/span&gt;ctivate.ps1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To confirm which Python you have in PowerShell use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Get-Command python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deactivate an environment
&lt;/h4&gt;

&lt;p&gt;We don't need to deactivate the environment because we are going to use it. But if you need to deactivate it later, just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;deactivate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Open your editor
&lt;/h2&gt;

&lt;p&gt;Open your editor and select that environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Studio Code
&lt;/h3&gt;

&lt;p&gt;If you have Visual Studio Code and a shell like Bash, you can just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;code ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In any case, make sure you select the environment you just created for your editor.&lt;/p&gt;

&lt;p&gt;If you use Visual Studio Code, make sure you have the &lt;a href="https://code.visualstudio.com/docs/python/python-tutorial#_install-visual-studio-code-and-the-python-extension"&gt;Python Extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can then create a dummy file &lt;code&gt;dummy.py&lt;/code&gt; and open it. That will make VS Code load the extension and show the Python environment used.&lt;/p&gt;

&lt;p&gt;In the lower left corner you will see the Python version used, if you click it, you can select a different one.&lt;/p&gt;

&lt;p&gt;After this, you can delete the &lt;code&gt;dummy.py&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  PyCharm
&lt;/h3&gt;

&lt;p&gt;If you use PyCharm as your editor, open it.&lt;/p&gt;

&lt;p&gt;Select your project directory &lt;code&gt;./apiapp/&lt;/code&gt; as the workspace.&lt;/p&gt;

&lt;p&gt;Then &lt;a href="https://www.jetbrains.com/help/pycharm/configuring-python-interpreter.html"&gt;Configure a Python interpreter&lt;/a&gt; for your project, and select the interpreter inside of the environment you just created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the correct environment
&lt;/h3&gt;

&lt;p&gt;Using the correct environment in your editor as we described here and opening it exactly in your project directory will make your editor know the installed packages and will let it provide autocompletion, type checks, relative imports, etc.&lt;/p&gt;

&lt;p&gt;If you didn't configure the environment correctly or if you didn't open it exactly in your project directory (for example, you open one directory above), your editor won't be able to give you all those features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create files
&lt;/h2&gt;

&lt;p&gt;Now, in your editor, create a directory &lt;code&gt;app&lt;/code&gt;. It will store all your actual code.&lt;/p&gt;

&lt;p&gt;Inside of that &lt;code&gt;app&lt;/code&gt; directory, create 2 empty files &lt;code&gt;main.py&lt;/code&gt; and &lt;code&gt;__init__.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And inside of your project directory, right next to the &lt;code&gt;app&lt;/code&gt; directory, create an empty &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Your file structure should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiapp
├── app
│   ├── __init__.py
│   └── main.py
├── requirements.txt
└── env
    └── ...

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Edit your &lt;code&gt;requirements.txt&lt;/code&gt; to have the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi
uvicorn
sqlalchemy
async-exit-stack
async-generator
passlib[bcrypt]
python-jose[cryptography]
python-multipart
python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install requirements
&lt;/h3&gt;

&lt;p&gt;Now install the requirements from that &lt;code&gt;requirements.txt&lt;/code&gt; file in the terminal with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dev requirements
&lt;/h3&gt;

&lt;p&gt;Now, to facilitate development, we'll also add extra packages that will help us during development.&lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;dev-requirements.txt&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;black
mypy
flake8
requests
pytest
pytest-cov
isort
autoflake
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install dev requirements
&lt;/h3&gt;

&lt;p&gt;And now install the development requirements in the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; dev-requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In VS Code
&lt;/h3&gt;

&lt;p&gt;Enable &lt;code&gt;Language Server&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;, &lt;code&gt;black&lt;/code&gt; in the settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reload editor
&lt;/h3&gt;

&lt;p&gt;You might need to reload your editor for it to be able to detect the newly installed packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reload environment
&lt;/h3&gt;

&lt;p&gt;Right after installing new Python packages in your environment, you should activate the environment again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you are on Windows use the equivalent command.&lt;/p&gt;

&lt;p&gt;This will ensure that packages that have a command, like &lt;code&gt;uvicorn&lt;/code&gt; will be available in your terminal.&lt;/p&gt;

&lt;p&gt;Make sure that &lt;code&gt;uvicorn&lt;/code&gt; is available and is the correct version after installing and re-activating the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which uvicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should show the &lt;code&gt;uvicorn&lt;/code&gt; from your environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Note: Other package managers
&lt;/h3&gt;

&lt;p&gt;If you used a different environment and package manager like Poetry or Pipenv, the &lt;code&gt;requirements.txt&lt;/code&gt; file would be a different file and it would be managed differently, but here we are using the simplest version with the pure/standard Python packages (&lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;, etc).&lt;/p&gt;

&lt;h2&gt;
  
  
  The app - version 1
&lt;/h2&gt;

&lt;p&gt;Now we are going to create the first version of our app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit - v1
&lt;/h3&gt;

&lt;p&gt;Edit the file &lt;code&gt;main.py&lt;/code&gt;...&lt;/p&gt;

&lt;h3&gt;
  
  
  Run - v1
&lt;/h3&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uvicorn app.main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;main.py&lt;/code&gt; again, Uvicorn auto-reloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Version
&lt;/h2&gt;

&lt;p&gt;The final version of the source code is here: &lt;a href="https://github.com/tiangolo/blog-posts/tree/master/pyconby-web-api-from-scratch-with-fastapi/apiapp"&gt;https://github.com/tiangolo/blog-posts/tree/master/pyconby-web-api-from-scratch-with-fastapi/apiapp&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional scripts
&lt;/h2&gt;

&lt;p&gt;There's a script to run the &lt;strong&gt;tests&lt;/strong&gt; and report coverage in HTML so that you can explore it in your browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash test.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there is a script to &lt;strong&gt;format&lt;/strong&gt; all the code automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash format.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;You can follow me, contact me, ask questions, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to start contributing to open source</title>
      <dc:creator>Sebastián Ramírez</dc:creator>
      <pubDate>Mon, 23 Dec 2019 07:01:39 +0000</pubDate>
      <link>https://dev.to/tiangolo/how-to-start-contributing-to-open-source-1jmg</link>
      <guid>https://dev.to/tiangolo/how-to-start-contributing-to-open-source-1jmg</guid>
      <description>&lt;p&gt;Here's a tip to help you get started contributing to &lt;strong&gt;open source&lt;/strong&gt; (if you haven't started yet).&lt;/p&gt;




&lt;p&gt;This article lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo/how-to-start-contributing-to-open-source-1jmg"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo/how-to-start-contributing-to-open-source-49488f3ad6e"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/blog-posts/blob/master/how-to-start-contributing-to-open-source/README.md"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;(too long, didn't read)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Newbies&lt;/strong&gt; are great at &lt;strong&gt;docs&lt;/strong&gt;, better than maintainers. Start with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find a problem
&lt;/h2&gt;

&lt;p&gt;First, find a problem that you want to solve, something that &lt;strong&gt;you care about&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you see you can solve it easily without technology, cool, go and do that. And come back with a new problem ;)&lt;/p&gt;

&lt;p&gt;Then find how technology could help to solve it, what kind of app or system would help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find a project
&lt;/h2&gt;

&lt;p&gt;Then find an open source project that would help to &lt;strong&gt;solve that problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If there's a tool that solves the whole problem, that's great. But it's more probable that you will have to divide the problem into smaller parts, and then find an open source tool that would help with &lt;strong&gt;some part of the problem&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Study it
&lt;/h2&gt;

&lt;p&gt;Study that open source tool.&lt;/p&gt;

&lt;p&gt;Read its &lt;strong&gt;documentation&lt;/strong&gt;, try the examples, learn how to use it.&lt;/p&gt;

&lt;p&gt;If it's solving a problem you care about (or part of it) it will be useful, it will be interesting to study. And having a real-world objective for that tool helps you put the &lt;strong&gt;focus&lt;/strong&gt; where it should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;While you study that tool, there are high chances that the documentation has obsolete parts, needs updates, clarifications, etc.&lt;/p&gt;

&lt;p&gt;Or simply, it could have been &lt;strong&gt;explained better&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, that you are just starting and have a &lt;strong&gt;fresh point of view&lt;/strong&gt; with that tool, it's the perfect moment to improve documentation.&lt;/p&gt;

&lt;p&gt;You need some steps first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn how to make a &lt;a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request"&gt;pull request&lt;/a&gt; with change suggestions.&lt;/li&gt;
&lt;li&gt;Learn about the docs for that project. How they are created. It's normally some text files in a directory in the same project, or in a sibling project. But you normally can just edit the small part that you found without having to learn everything about that project's docs system.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://help.github.com/en/github/getting-started-with-github/fork-a-repo"&gt;Fork that project&lt;/a&gt; to get your own version to change and propose changes to the original.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Update the docs
&lt;/h3&gt;

&lt;p&gt;Now add documentation for that project.&lt;/p&gt;

&lt;p&gt;Add the examples that would have helped &lt;em&gt;you&lt;/em&gt; get started, with a focus on a simple real-life problem (like yours) that this tool would help solve.&lt;/p&gt;

&lt;p&gt;Add the text that would have explained to &lt;em&gt;you&lt;/em&gt; the &lt;strong&gt;same concepts more easily&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Add the &lt;strong&gt;definitions&lt;/strong&gt; of the words the maintainer assumed were obvious but were required to know to be able to do the first steps.&lt;/p&gt;

&lt;p&gt;Add documentation for the things that aren't documented yet.&lt;/p&gt;

&lt;p&gt;Update the things that seem &lt;strong&gt;complex or difficult&lt;/strong&gt;. Once you &lt;strong&gt;understand&lt;/strong&gt; them, think how someone could have explained them better to &lt;em&gt;you&lt;/em&gt;, and write that.&lt;/p&gt;

&lt;p&gt;Fix typos. There are always typos.&lt;/p&gt;




&lt;p&gt;Any of those changes would probably be an &lt;strong&gt;independent pull request&lt;/strong&gt;. Keep them separated, with the minimum changes, with a very clear scope. That helps maintainers accept just the things they are fine with.&lt;/p&gt;

&lt;p&gt;After that, you will already have a bunch of contributions to open source. From there on it's a lot easier.&lt;/p&gt;

&lt;p&gt;But that's a good way to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  The newbie advantage
&lt;/h2&gt;

&lt;p&gt;If you are a "newbie" with that open source tool, if you are just getting started with it, you have a great advantage over the maintainers or any other more experienced developer that uses that tool. And it's exactly that. That you are a &lt;em&gt;newbie&lt;/em&gt;... you know nothing about that tool.&lt;/p&gt;

&lt;p&gt;That means that you have a completely &lt;strong&gt;fresh point of view&lt;/strong&gt;. You don't take anything for granted with that tool. You don't assume as obvious some basic concepts of the tool because you weren't the same person developing it for months, knowing all its internal workings.&lt;/p&gt;

&lt;p&gt;Also, you are actually &lt;em&gt;reading&lt;/em&gt; the docs. And you are reading them &lt;strong&gt;in order&lt;/strong&gt;. So, you can more easily spot things that could have been explained before or better. You can find inconsistencies in different parts.&lt;/p&gt;

&lt;p&gt;And you won't be reading it "like you know it", it won't be the 30th time you read those same docs, it will be the first. So, you will more easily be able to detect incorrect things, typos, etc.&lt;/p&gt;

&lt;p&gt;A maintainer, an experienced user, or a contributor won't normally spend much time reading the docs again and again, so, those "small" errors accumulate.&lt;/p&gt;

&lt;p&gt;And a maintainer or the project creator will never have a &lt;em&gt;fresh&lt;/em&gt; point of view, without all the background of having built the whole tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A newbie has a fresh point of view&lt;/strong&gt;. And that's an advantage.&lt;/p&gt;

&lt;p&gt;Start with docs!&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;You can follow me, contact me, ask questions, see what I do, or use my open source code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tiangolo"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/tiangolo/"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tiangolo"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tiangolo"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
