Application & Request Context (g, current_app)

Reviewed & published by Brayan K

A Flask context is the temporary state Flask sets up around each request so globals like current_app, g, and request point to the right things.

Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll learn how the application context and request context differ, how to stash per-request data on g, how to reach the active app through current_app, why the "Working outside of application context" error appears, and the two ways to fix it.

What You'll Learn in This Lesson

1 Two Contexts and the Error You'll Hit

Flask gives you a handful of "magic" globals — current_app, g, request, and session — that always seem to point at the right thing. They work because Flask wraps each request in two layers of state. The application context binds current_app and g to the app that is handling the work, and the request context binds request and session to the specific HTTP request being served.

The application context lives for the length of a request or any block you open with app.app_context(). The request context lives for exactly one HTTP request. During a normal request Flask pushes the application context first, then the request context on top, so both are available everywhere in your view code.

The catch: these proxies are useless when there is no context active — for example at import time, inside a plain script, or in a background thread. Touch current_app in that situation and Flask raises a clear RuntimeError: Working outside of application context. Pushing a context with app.app_context() fixes it.

# app.py — the "outside of context" error and the fix
from flask import Flask, current_app

app = Flask(__name__)
app.config["APP_NAME"] = "Demo"

# Touching current_app with NO active context raises a clear error:
try:
    print(current_app.config["APP_NAME"])
except RuntimeError as e:
    print("Error:", str(e).split(".")[0])   # Error: Working outside of application context

# app_context() pushes an application context so current_app works:
with app.app_context():
    print(current_app.config["APP_NAME"])    # Demo

# ✅ Expected output:
# Error: Working outside of application context
# Demo

The first access fails because nothing has pushed a context yet; the second succeeds because with app.app_context(): makes current_app point at your app for the duration of the block.

2 Sharing Per-Request Data with flask.g

g (short for "globals") is a per-request scratchpad. It is a place to compute something once — the logged-in user, a database connection, a request id — and read it back anywhere else in the same request without threading the value through every function call.

A common pattern is to populate g in a before_request hook so it is ready before any view runs. The view, its helpers, and even template context functions can then read g.user. Because current_app and request live in the same contexts, you can mix all three freely inside a request.

Remember the lifetime: g is wiped clean at the start of every request. It is perfect for "data for this request" and wrong for "data that should survive between requests."

# app.py — set on g in before_request, read it in the view
from flask import Flask, g, current_app, request

app = Flask(__name__)
app.config["APP_NAME"] = "Demo"

@app.before_request
def load_user():
    # g is a per-request scratchpad shared across functions in one request
    g.user = "ada"

@app.route("/whoami")
def whoami():
    return f"{g.user} on {current_app.config['APP_NAME']} at {request.path}"

with app.test_client() as client:
    print(client.get("/whoami").data.decode())   # ada on Demo at /whoami

# ✅ Expected output:
# ada on Demo at /whoami

The view never received user as an argument — it pulled it off g, which the before_request hook filled in moments earlier, all within the same request context.

3 Faking a Request with test_request_context()

Sometimes you need request or session outside of a live server — in a unit test, a REPL, or a helper you want to exercise in isolation. app.test_request_context(path) pushes both an application context and a request context for the URL you give it, so request, g, current_app, and session all work.

Inside the block, request.path and request.args reflect the URL you passed. You can also use g.get("key", default) to read an attribute that may not be set yet — it returns the default instead of raising AttributeError, which is handy when you are not sure a hook has run.

# app.py — fake a request so request/g work without a server
from flask import Flask, request, g

app = Flask(__name__)

# test_request_context() fakes a request so request/g work without a server
with app.test_request_context("/search?q=flask"):
    print(request.path)              # /search
    print(request.args.get("q"))     # flask
    # g.get returns a default when the attribute isn't set yet
    print(g.get("user", "anonymous"))  # anonymous

# ✅ Expected output:
# /search
# flask
# anonymous

No server, no test client — test_request_context() alone is enough to make the request proxies behave, which is why it is so useful in tests and scripts.

🔬 Context Cheat Sheet

You want...Use
The current appcurrent_app
A per-request storeg
The requestrequest
Push an app contextwith app.app_context():
Fake a requestwith app.test_request_context():

Rule of thumb: need only current_app and g → app context. Also need request or session → request context.

🎯 Your Turn: Fill in the Blanks

Complete the code so a before_request hook stashes a request id on g and the view reads it back alongside the app's name. Replace each ___.

# 🎯 YOUR TURN: replace each ___ to use g and current_app
from flask import Flask, g, current_app

app = Flask("tracker")

@app.before_request
def assign_request_id():
    ___.request_id = "req-001"          # stash a request id on the per-request store

@app.route("/ping")
def ping():
    return f"{g.request_id} handled by {___.name}"   # read the active app's name

with app.test_client() as client:
    print(client.get("/ping").data.decode())   # req-001 handled by tracker

Common Errors & Quick Reference

❌ "Working outside of application context"

You touched current_app or g with no context active — usually at import time, in a script, or in a background thread. Wrap the code in with app.app_context(): (or with app.test_request_context(): if you also need request).

❌ Using g to store data across different requests

g resets at the start of every request, so values you set in one request are gone in the next. For data that must persist, use a database, a cache, or a signed session — not g.

TaskCode
Reach the active appcurrent_app
Read g safelyg.get("user", None)
Push an app contextwith app.app_context():
Fake a requestwith app.test_request_context():

🏆 Mini Challenge: Compute Outside a Request

Reach current_app.config outside of any request and cache a computed value on g within the same context.

# 🏆 Mini Challenge: app_context() + g outside a request
from flask import Flask, g, current_app

app = Flask("billing")
app.config["RATE"] = 7

with app.app_context():
    # Read config outside a request via the application context
    rate = current_app.config["RATE"]
    # Cache a computed value on g within this context
    g.total = rate * 6
    print(f"{current_app.name}: rate={rate}, cached total={g.total}")
    # g.total is reused, not recomputed
    print("cached again:", g.total)

# Expected:
# billing: rate=7, cached total=42
# cached again: 42

❓ Frequently Asked Questions

Lesson complete — the magic globals aren't magic anymore!

You understand the difference between the application context (current_app, g) and the request context (request, session), why "Working outside of application context" happens, and how app.app_context() and app.test_request_context() fix it.

Practice quiz

What does the application context bind?

  • request and session
  • current_app and g
  • url_for and redirect
  • jsonify and abort

Answer: current_app and g. The application context binds current_app and g to the active app.

What does the request context bind?

  • current_app and g
  • config and extensions
  • request and session
  • Blueprint and url_prefix

Answer: request and session. The request context binds request and session to a specific HTTP request.

Which error appears when you touch current_app with no context active?

  • Working outside of application context
  • 404 Not Found
  • Method Not Allowed
  • Internal Server Error

Answer: Working outside of application context. Flask raises RuntimeError: Working outside of application context.

What is flask.g best used for?

  • Caching data across many requests
  • Per-request scratch storage shared across functions
  • Storing settings permanently
  • Signing session cookies

Answer: Per-request scratch storage shared across functions. g is a per-request scratchpad, reset on every request.

Which call pushes only an application context?

  • app.test_request_context()
  • app.test_client()
  • app.run()
  • app.app_context()

Answer: app.app_context(). app.app_context() pushes an application context so current_app and g work.

Which call gives you request and session outside a server?

  • app.app_context()
  • app.test_request_context()
  • current_app.config
  • g.get()

Answer: app.test_request_context(). test_request_context() pushes both an app and a request context.

When is g reset?

  • At the start of every request
  • Once per app startup
  • When the server shuts down
  • Never, it persists

Answer: At the start of every request. g is wiped clean at the start of every request.

During a normal request, which context is pushed first?

  • The request context
  • The session context
  • The application context
  • They are pushed simultaneously

Answer: The application context. Flask pushes the application context first, then the request context on top.

How do you read g safely when an attribute may not be set?

  • g.user always works
  • g['user']
  • g.get('user', default)
  • g.fetch('user')

Answer: g.get('user', default). g.get('key', default) returns the default instead of raising AttributeError.

You only need current_app and g in a script. Which context fits?

  • A request context
  • The application context
  • A test client
  • No context needed

Answer: The application context. Need only current_app and g means the application context is enough.

Continue this course

Frequently asked questions

What is the difference between the application context and the request context?

The application context binds current_app and g to the active app, and it lives for the duration of a request or any block you open with app.app_context(). The request context binds request and session to a specific incoming HTTP request and lives only for that one request. Flask pushes the application context first, then the request context on top of it, so every request automatically has both. Outside of a request — at import time, in a script, or in a background thread — neither exists until you push one yourself.

What is flask.g used for?

g is a per-request scratchpad: a place to stash data you compute once and reuse across functions during a single request, such as the current user, a database connection, or a request id. A before_request hook can set g.user and every view, helper, and template-context function in that same request can read it. g is reset on every request, so it is never a safe place to cache data that should survive between different requests.

Why do I get 'Working outside of application context'?

Flask raises that RuntimeError when you touch a context-bound proxy — current_app or g — while no application context is active. It usually happens when you access these at module import time, inside a plain script with no app running, in a Celery task, or in a background thread. The fix is to push a context explicitly: wrap the code in 'with app.app_context():' for app-level work, or 'with app.test_request_context():' when you also need request or session.

When should I use app_context() versus test_request_context()?

Use app.app_context() when you only need current_app and g — for example running database setup, a CLI command, or a scheduled job that reads configuration. Use app.test_request_context() when your code also needs request or session, such as calling url_for, inspecting request.args, or testing a view's logic without starting a real server. test_request_context() pushes both an application context and a request context, so everything works.