question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Globally importable request object

See original GitHub issue

Hello, I had an issue with Starlette where I had to access request information outside of a view function. I successfully fixed my issue and would like to share the discussion I had with @tomchristie and how I solved the issue.


Here’s a snippet of the question I asked and the answer I got:

From me:

Hi!

I am currently working with Starlette (porting over a Flask project to FastAPI) and I’m wondering if there’s some way to get the current request from anywhere in the code, like you can do with Flask using from flask import request. I need to access data from the request but from a non-view part of the code (a logging filter, so I can’t even pass it along).

I’ve read the whole documentation and looked at all the GitHub issues but couldn’t find anything that fits my needs, this https://github.com/encode/starlette/issues/379 is the closest I found, which seems to be part of what I want. However I found no way of importing the request object so it’s useless to me.

Reply: No we don’t currently do that. Using contextvars would be the way to implement it. You could do this in a middleware class without having to alter Starlette directly. Happy to chat through how you’d achieve that, but let’s open a ticket if there’s more to talk over here. 😃


Solution

RequestContextMiddleware:

from contextvars import ContextVar
from uuid import uuid4

from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request


REQUEST_ID_CTX_KEY = "request_id"

_request_id_ctx_var: ContextVar[str] = ContextVar(REQUEST_ID_CTX_KEY, default=None)


def get_request_id() -> str:
    return _request_id_ctx_var.get()


class RequestContextMiddleware(BaseHTTPMiddleware):

    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ):
        request_id = _request_id_ctx_var.set(str(uuid4()))

        response = await call_next(request)

        _request_id_ctx_var.reset(request_id)

        return response

In your app initialisation:

app.add_middleware(RequestContextMiddleware)

Usage: Now you will be able to import the get_request_id function anywhere and get the current request ID, or None if you are not in the context of a request.

You can also add many more context vars by creating new ContextVar objects, and using them the same way as _request_id_ctx_var in my example.


For the future

Maybe Starlette could expose an object that abstracts the context vars in some way. But my idea on this isn’t clear, I don’t know how generic this could be.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:17
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

10reactions
tomwojcikcommented, Dec 28, 2019

Fantastic, want to open a PR adding it to the “Third Party Packages” docs?

There goes my first PR in OSS!

https://github.com/encode/starlette/pull/770

6reactions
gareth-leakecommented, Jun 17, 2022

Came across a similar issue as @MarcDufresne. I am using FastAPI (thanks @tiangolo), and needed a way to access information on the request object outside of a view. I initially looked at using starlette-context (thanks @tomwojcik) but found the below solution to work for my needs.

Marc, first off just wanna say thanks so much for this solution! It got me started on the right path. Wanted to post an update here, for anyone having a similar issue in 2022.

Several months after this initial solution, the authors warn against using BaseHTTPMiddleware – the parent class Marc’s middleware inherits from.

Instead, the suggestion is to use a raw ASGI middleware. However, there isn’t much documentation for this. I was able to use Starlette’s AuthenticationMiddleware as a reference point, and develop what I needed in combination with Marc’s wonderful solution of ContextVars.

# middleware.py
from starlette.types import ASGIApp, Receive, Scope, Send

REQUEST_ID_CTX_KEY = "request_id"

_request_id_ctx_var: ContextVar[str] = ContextVar(REQUEST_ID_CTX_KEY, default=None)

def get_request_id() -> str:
    return _request_id_ctx_var.get()

class CustomRequestMiddleware:
    def __init__(
        self,
        app: ASGIApp,
    ) -> None:
        self.app = app

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        if scope["type"] not in ["http", "websocket"]:
            await self.app(scope, receive, send)
            return

        request_id = _request_id_ctx_var.set(str(uuid4()))

        await self.app(scope, receive, send)

        _request_id_ctx_var.reset(request_id)

And then in the app setup:

# main.py
app.add_middleware(CustomRequestMiddleware)

And finally, the non-view function:

# myfunc.py
import get_request_id

request_id = get_request_id()

Thanks again to everyone in this thread for all the help, and I hope the above is useful!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is the Global Request variable in Python/Django available?
It provides a way to get the request from anywhere once the request has been constructed by Django in the first place. It...
Read more >
The Global Object Pattern - Python Design Patterns
Un-indented assignment statements, expressions, and even loops and conditionals will execute as the module is imported.
Read more >
Global request object on ASGI - Async - Django Forum
Hi, I need to implement global request object function for purpose of my project: for simple sync mode under WSGI - we are...
Read more >
Global-Object.require — MarkLogic 10 Product Documentation
Imports a module at the specified location, and returns a JavaScript object. Parameters. location, Location of the module to import. Usage Notes. The...
Read more >
import - JavaScript - MDN Web Docs - Mozilla
The static import declaration is used to import read-only live bindings which are exported by another module. The imported bindings are ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found