Today I Learned

Flask's `g` - The "Global" That Isn't

Today I learned why Flask's request context object is called g, and it's too clever by half.

I was reviewing a Flask codebase and kept seeing this pattern:

from flask import g

def get_db():
    if "db" not in g:
        g.db = sqlite3.connect("app.db")
    return g.db

I always assumed g was just an arbitrary short name, but it turns out it stands for "global" - with a twist. It's intentionally ironic: g provides global-like access but is actually request-local. Each request gets its own isolated g instance.

The Problem It Solves

Without g, you might be tempted to use actual globals:

# DON'T DO THIS - Shared between ALL requests!
db_connection = None

@app.route("/users/<user_id>")
def get_user(user_id):
    global db_connection
    if not db_connection:
        db_connection = sqlite3.connect("app.db")
    # Race conditions! Data leaks! Connection errors!

This is a disaster waiting to happen. Multiple concurrent requests would compete for the same connection, potentially mixing user data or causing crashes.

How g Actually Works

Flask's g gives you what feels like a global variable but is actually isolated per request:

@app.route("/users/<user_id>")
def get_user(user_id):
    db = get_db()  # Gets THIS request's connection
    # Safe, isolated, no interference with other requests

The lifecycle:

  1. Request arrives → Flask creates a fresh g object
  2. You store stuff in it → g.db = connection
  3. Access it anywhere in that request → return g.db
  4. Request ends → g is destroyed, cleanup runs

The Name Is Part of the Lesson

Armin Ronacher (Flask's creator) chose g deliberately. It's a subtle joke about what developers think they want (global state) versus what they actually need (request-scoped state). The single letter makes it:

  • Quick to type (you'll use it constantly)
  • Easy to remember ("g for global")
  • Obviously special (like e for exception)

Practical Example

Here's a real pattern I now will use for managing multiple resources:

from flask import g

def get_redis():
    if "redis" not in g:
        g.redis = redis.StrictRedis()
    return g.redis

def get_user():
    if "user" not in g:
        g.user = load_user_from_token()
    return g.user

@app.teardown_appcontext
def cleanup(error):
    # Always runs, even if request fails
    if hasattr(g, "redis"):
        g.redis.close()

The Key Insight

g is "global" in the sense that it's globally accessible within your request handling code, but it's actually providing thread-safe, request-isolated storage.

Tags:

flask python API