{"id":1820,"date":"2025-02-13T07:02:21","date_gmt":"2025-02-13T07:02:21","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/02\/13\/manage-environment-variables-with-pydantic\/"},"modified":"2025-02-13T07:02:21","modified_gmt":"2025-02-13T07:02:21","slug":"manage-environment-variables-with-pydantic","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/02\/13\/manage-environment-variables-with-pydantic\/","title":{"rendered":"Manage Environment Variables with Pydantic"},"content":{"rendered":"<p>    Manage Environment Variables with Pydantic<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<h2 class=\"wp-block-heading\" id=\"a4a0\">Introduction<\/h2>\n<p class=\"wp-block-paragraph\" id=\"c5e3\">Developers work on applications that are supposed to be deployed on some server in order to allow anyone to use those. Typically in the machine where these apps live, developers set up environment variables that allow the app to run. These variables can be API keys of external services, URL of your database and much more.<\/p>\n<p class=\"wp-block-paragraph\" id=\"e959\">For local development though, it is really inconvenient to declare these variables on the machine because it is a slow and messy process. So I\u2019d like to share in this short tutorial how to use <a href=\"https:\/\/towardsdatascience.com\/tag\/pydantic\/\" title=\"Pydantic\">Pydantic<\/a> to handle environment variables in a secure way.<\/p>\n<h2 class=\"wp-block-heading\" id=\"c1e8\">.env file<\/h2>\n<p class=\"wp-block-paragraph\" id=\"c92b\">What you commonly do in a <a href=\"https:\/\/towardsdatascience.com\/tag\/python\/\" title=\"Python\">Python<\/a> project is to store all your environment variables in a file named .env. This is a text file containing all the variables in a\u00a0<code>key : value<\/code>\u00a0format. You can use also the value of one of the variables to declare one of the other variables by leveraging the\u00a0<code>{}<\/code>\u00a0syntax.<\/p>\n<p class=\"wp-block-paragraph\" id=\"88bc\">The following is an example:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">#.env file\n\nOPENAI_API_KEY=\"sk-your private key\"\nOPENAI_MODEL_ID=\"gpt-4o-mini\"\n\n# Development settings\nDOMAIN=example.org\nADMIN_EMAIL=admin@${DOMAIN}\n\nWANDB_API_KEY=\"your-private-key\"\nWANDB_PROJECT=\"myproject\"\nWANDB_ENTITY=\"my-entity\"\n\nSERPAPI_KEY= \"your-api-key\"\nPERPLEXITY_TOKEN = \"your-api-token\"<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"1a0e\">Be aware the .env file should remain private, so it is important that this file is mentioned in your\u00a0<strong>.gitignore<\/strong>\u00a0file, to be sure that you\u00a0<strong>never push it on GitHub<\/strong>, otherwise, other developers could steal your keys and use the tools you\u2019ve paid for.<\/p>\n<h2 class=\"wp-block-heading\" id=\"26f9\">env.example file<\/h2>\n<p class=\"wp-block-paragraph\" id=\"39ec\">To ease the life of developers who will clone your repository, you could include an env.example file in your project. This is a file containing only the keys of what is supposed to go into the .env file. In this way, other people know what APIs, tokens, or secrets in general they need to set to make the scripts work.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">#env.example\n\nOPENAI_API_KEY=\"\"\nOPENAI_MODEL_ID=\"\"\n\nDOMAIN=\"\"\nADMIN_EMAIL=\"\"\n\nWANDB_API_KEY=\"\"\nWANDB_PROJECT=\"\"\nWANDB_ENTITY=\"\"\n\nSERPAPI_KEY= \"\"\nPERPLEXITY_TOKEN = \"\"<\/code><\/pre>\n<h1 class=\"wp-block-heading\" id=\"9269\">python-dotenv<\/h1>\n<p class=\"wp-block-paragraph\" id=\"6671\">python-dotenv is the library you use to load the variables declared into the .env file. To install this library:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">pip install python-dotenv<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"c5f1\">Now you can use the load_dotenv to load the variables. Then get a reference to these variables with the os module.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">import os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nOPENAI_API_KEY = os.getenv('OPENAI_API_KEY')\nOPENAI_MODEL_ID = os.getenv('OPENAI_MODEL_ID')<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"3edf\">This method will first look into your .env file to load the variables you\u2019ve declared there. If this file doesn\u2019t exist, the variable will be taken from the host machine. This means that you can use the .env file for your local development but then when the code is deployed to a host environment like a virtual machine or Docker container we are going to directly use the environment variables defined in the host environment.<\/p>\n<h2 class=\"wp-block-heading\" id=\"fa7e\">Pydantic<\/h2>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\" id=\"b885\">Pydantic is one of the most used libraries in Python for data validation. It is also used for serializing and deserializing classes into JSON and back. It automatically generates JSON schema, reducing the need for manual schema management. It also provides built-in data validation, ensuring that the serialized data adheres to the expected format. Lastly, it easily integrates with popular web frameworks like FastAPI.<\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\" id=\"e51f\"><a href=\"https:\/\/github.com\/pydantic\/pydantic-settings\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>pydantic-settings<\/strong><\/a><strong>\u00a0<\/strong>is a Pydantic feature needed to load and validate settings or config classes from environment variables.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">!pip install pydantic-settings<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"81ed\">We are going to create a class named\u00a0<code>Settings<\/code>. This class will inherit\u00a0<code>BaseSettings<\/code><em>.\u00a0<\/em>This makes the default behaviours of determining the values of any fields to be read from the .env file. If no var is found in the .env file it will be used the default value if provided.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">from pydantic_settings import BaseSettings, SettingsConfigDict\n\nfrom pydantic import (\n    AliasChoices,\n    Field,\n    RedisDsn,\n)\n\n\nclass Settings(BaseSettings):\n    auth_key: str = Field(validation_alias='my_auth_key')  \n    api_key: str = Field(alias='my_api_key')  \n\n    redis_dsn: RedisDsn = Field(\n        'redis:\/\/user:pass@localhost:6379\/1', #default value\n        validation_alias=AliasChoices('service_redis_dsn', 'redis_url'),  \n    )\n\n    model_config = SettingsConfigDict(env_prefix='my_prefix_')<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"8173\">In the\u00a0<code>Settings<\/code>\u00a0class above we have defined several fields. The\u00a0<code>Field<\/code>\u00a0class is used to provide\u00a0<a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/fields\/#pydantic.fields.Field\" rel=\"noreferrer noopener\" target=\"_blank\">extra information<\/a>\u00a0about an attribute.<\/p>\n<p class=\"wp-block-paragraph\" id=\"d847\">In our case, we setup a\u00a0<code>validation_alias<\/code>. So the variable name to look for in the .env file is overridden. In the case reported above, the environment variable\u00a0<em>my_auth_key<\/em>\u00a0will be read instead of\u00a0<em>auth_key<\/em>.<\/p>\n<p class=\"wp-block-paragraph\" id=\"8381\">You can also have multiple aliases to look for in the .env file that you can specify by leveraging\u00a0<code>AliasChoises(choise1, choise2).<\/code><\/p>\n<p class=\"wp-block-paragraph\" id=\"e0c6\">The last attribute\u00a0<code>model_config<\/code>\u00a0, contains all the variables regarding a particular topic (e.g connection to a db). And this variable will store all .env var that start with the prefix\u00a0<code>env_prefix.<\/code><\/p>\n<h2 class=\"wp-block-heading\" id=\"d90d\">Instantiate and use settings<\/h2>\n<p class=\"wp-block-paragraph\" id=\"15bc\">The next step would be to actually instantiate and use these settings in your Python project.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">from pydantic_settings import BaseSettings, SettingsConfigDict\n\nfrom pydantic import (\n    AliasChoices,\n    Field,\n    RedisDsn,\n)\n\n\nclass Settings(BaseSettings):\n    auth_key: str = Field(validation_alias='my_auth_key')  \n    api_key: str = Field(alias='my_api_key')  \n\n    redis_dsn: RedisDsn = Field(\n        'redis:\/\/user:pass@localhost:6379\/1', #default value\n        validation_alias=AliasChoices('service_redis_dsn', 'redis_url'),  \n    )\n\n    model_config = SettingsConfigDict(env_prefix='my_prefix_')\n\n# create immediately a settings object\nsettings = Settings()<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"28c7\">Now what use the settings in other parts of our codebase.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">from Settings import settings\n\nprint(settings.auth_key) <\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"7485\">You finally have an easy access to your settings, and Pydantic helps you validate that the secrets have the correct format. For more advanced validation tips refer to the Pydantic documentation:\u00a0<a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noreferrer noopener\" target=\"_blank\">https:\/\/docs.pydantic.dev\/latest\/<\/a><\/p>\n<h2 class=\"wp-block-heading\" id=\"cfc0\"><strong>Final thoughts<\/strong><\/h2>\n<p class=\"wp-block-paragraph\" id=\"2119\">Managing the configuration of a project is a boring but important part of software development. Secrets like API keys, db connections are what usually power your application. Naively you can hardcode these variables in your code and it will still work, but for obvious reasons, this could not be a good practice. In this article, I showed you an introduction on how to use pydantic settings to have a structured and safe way to handle your configurations.<\/p>\n<p class=\"wp-block-paragraph\" id=\"a516\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/1f4bc.png?ssl=1\" alt=\"\ud83d\udcbc\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\">\u00a0<a href=\"https:\/\/www.linkedin.com\/in\/marcello-politi\/\" target=\"_blank\" rel=\"noreferrer noopener\">Linkedin<\/a>\u00a0| <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/1f426.png?ssl=1\" alt=\"\ud83d\udc26\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\">\u00a0<a href=\"https:\/\/x.com\/Marcello_AI\" target=\"_blank\" rel=\"noreferrer noopener\">X (Twitter)<\/a>\u00a0|\u00a0<a href=\"https:\/\/emojiterra.com\/laptop-computer\/\" target=\"_blank\" rel=\"noreferrer noopener\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/1f4bb.png?ssl=1\" alt=\"\ud83d\udcbb\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\"><\/a>\u00a0<a href=\"https:\/\/marcello-politi.super.site\/\" target=\"_blank\" rel=\"noreferrer noopener\">Website<\/a><\/p>\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/manage-environment-variables-with-pydantic\/\">Manage Environment Variables with Pydantic<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Marcello Politi<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/manage-environment-variables-with-pydantic\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Manage Environment Variables with Pydantic Introduction Developers work on applications that are supposed to be deployed on some server in order to allow anyone to use those. Typically in the machine where these apps live, developers set up environment variables that allow the app to run. These variables can be API keys of external services, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,1726,160,1727,1728,157,302],"tags":[1729,258,163],"class_list":["post-1820","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-data-validation","category-programming","category-project-management","category-pydantic","category-python","category-software-engineering","tag-file","tag-variables","tag-your"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1820"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=1820"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1820\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=1820"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=1820"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=1820"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}