Skip to Content
Course content

333: Managing Configuration with python-dotenv

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this happen to almost every junior dev I've mentored. You're building a project, you've got your API keys working perfectly on your laptop, and then you push the code to GitHub. Suddenly, you get a frantic notification from a security bot telling you that your AWS secret key is public. Or, even worse, you deploy the code to a server and the app crashes immediately because the environment variables you set locally aren't present in the production shell.

import os
import requests

# I'm trying to be secure by using environment variables
API_KEY = os.getenv("WEATHER_API_KEY")
CITY = "Seattle"

def get_weather():
    # This line crashes with a 401 Unauthorized or a TypeError 
    # because API_KEY is actually None
    url = f"https://api.weatherapi.com/v1/current.json?key={API_KEY}&q={CITY}"
    response = requests.get(url)
    return response.json()

print(get_weather())

The "NoneType" configuration crash

If you run the code above, it likely won't throw a Python error immediately, but the API call will fail. Why? Because os.getenv() doesn't magically know where your secrets are; it only looks at the environment variables currently loaded into your operating system's process. If you haven't manually run export WEATHER_API_KEY=your_key in your terminal before launching the script, os.getenv simply returns None.

The mistake here is assuming that having a .env file sitting in your project folder is enough. Python doesn't read .env files by default. It's just a text file to the OS. You need a bridge to pull those key-value pairs from the file and inject them into the environment.

Bridging the gap with load_dotenv

This is where python-dotenv comes in. It's a tiny library that does one thing: it reads a file (usually named .env) and loads those variables into os.environ so that os.getenv() can actually find them.

First, you'll need to install it: pip install python-dotenv. Then, you just add two lines to the top of your entry point script.

import os
import requests
from dotenv import load_dotenv

# This is the magic line. It looks for a .env file in the current directory
# and loads the variables into the environment.
load_dotenv()

API_KEY = os.getenv("WEATHER_API_KEY")
CITY = "Seattle"

def get_weather():
    if not API_KEY:
        raise ValueError("Missing WEATHER_API_KEY! Did you forget the .env file?")
    
    url = f"https://api.weatherapi.com/v1/current.json?key={API_KEY}&q={CITY}"
    response = requests.get(url)
    return response.json()

print(get_weather())

I added a small check for if not API_KEY. I highly recommend doing this. It's much better to have your app crash with a clear "Missing API Key" message than to spend an hour wondering why your API is returning "Unauthorized" errors.

Keeping your secrets out of Git

Now, here is the most important part of this entire workflow. Your .env file contains secrets. If you commit that file to version control, you've defeated the whole purpose of using environment variables.

I always tell my team to create two files. First, the .env file which stays on your machine. Second, a .env.example file. The example file contains the keys but not the actual values.

Your .env.example should look like this:

# .env.example
WEATHER_API_KEY=your_api_key_here
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

Then, you add .env to your .gitignore file. This way, when a new developer joins your project, they see the example file, copy it to a new .env, and plug in their own keys. It's the industry standard for a reason—it keeps the codebase portable and the secrets private.




📋 Practical Task

Secure a Database Connection String for a User Profile Service

You are building a user profile service that connects to a PostgreSQL database. Currently, the database URL is hardcoded, which is a huge security risk. Your task is to migrate this to a secure configuration using python-dotenv.

Requirements:

  • Create a file named .env containing a variable DB_URL set to postgresql://admin:secret_password@localhost:5432/user_db.
  • Create a .gitignore file that ensures the .env file is never tracked by Git.
  • Write a Python script that:
    • Loads the environment variables from the .env file.
    • Retrieves the DB_URL using os.getenv.
    • Prints a confirmation message: "Connected to database at: [URL]".
    • If DB_URL is missing, prints "Error: Database configuration not found." and exits.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.