Configuring Environment Variables with Starlette
October 17, 2021
Overview
When working with applications and services it is often necessary to configure various settings in order for the application to operate. This can be non-sensitive information like logging or directory paths, or sensitive information like passwords, secrets and database connections.
It is considered good practice to separate configuration settings from code. This may be done by storing configuration settings in environment variables on the OS server where the application is deployed. By doing so, you avoid hard-coding and commiting sensitive information to source control and provide an easy mechansim to define configuration settings at deployment.
The .env
File
The .env
(dot env) file is a simple text file that contains key-value pairs used to store and load environment variables. The key is the name of the environment variable and the value is the value of that variable. The .env
file is stored at the root of your application and must not be committed to source control (add it to your .gitignore
file for peace of mind).
An example .env
file may look like this:
USERNAME=luke
PASSWORD=qwerty
SECRET_KEY=123456789
Starlette Configuration
Starlette is a lightweight ASGI framework used for building high performance asyncio services. Starlette has a useful built-in Config
class used to load environment variables into an application.
The Config
class allows you load configuration variables from environment variables, a .env
file and local defaults (with precedence given to this order if the same keys exist across the different sources).
As an example, we can initialise a Config
object and then use it to load module-level variables as follows:
from starlette.config import Config
from starlette.datastructures import Secret
config = Config(".env")
USERNAME = config("USERNAME", cast=str, default="luke")
PASSWORD = config("PASSWORD", cast=Secret)
SECRET_KEY = config("SECRET_KEY", cast=Secret)
Starlette allows you to cast the variables being loaded to conform to specific data types. Furthermore, it provides custom data structures including a Secret
class (for obscuring sensitive information) and a CommaSeparatedStrings
class (for loading comma-separated values from the .env
file).
When we want to use these configuration variables, we can import the config
module and use the module-level variables that have been defined:
from app import config
application = some_application(
username=config.USERNAME,
password=config.PASSWORD,
secret_key=config.SECRET_KEY
)
If we have tests that use these configuration variables, we can overwrite them by setting custom environment variables using Starlette's environ
instance. This helps prevent production settings being used in a testing environment.
from starlette.config import environ
environ["USERNAME"] = "test_username"
environ["PASSWORD"] = "test_password"
environ["SECRET_KEY"] = "test_secret_key"