<h3 dir="auto">First Check</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I added a very descriptive title to this issue.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I used the GitHub search to find a similar issue and didn't find it.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I searched the FastAPI documentation, with the integrated search.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already searched in Google "How to X in FastAPI" and didn't find any information.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already read and followed all the tutorial in the docs and didn't find an answer.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/samuelcolvin/pydantic">Pydantic</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/swagger-api/swagger-ui">Swagger UI</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/Redocly/redoc">ReDoc</a>.</li>
</ul>
<h3 dir="auto">Commit to Help</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I commit to help with one of those options 👆</li>
</ul>
<h3 dir="auto">Example Code</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from fastapi import FastAPI

app = FastAPI()

@app.post(&quot;/items:queue&quot;)
def queue_new_item(item: dict):
    return &quot;An item has been queued for later inclusion.&quot;"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">fastapi</span> <span class="pl-k">import</span> <span class="pl-v">FastAPI</span>

<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">FastAPI</span>()

<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">post</span>(<span class="pl-s">"/items:queue"</span>)</span>
<span class="pl-k">def</span> <span class="pl-en">queue_new_item</span>(<span class="pl-s1">item</span>: <span class="pl-s1">dict</span>):
    <span class="pl-k">return</span> <span class="pl-s">"An item has been queued for later inclusion."</span></pre></div>
<h3 dir="auto">Description</h3>
<ul dir="auto">
<li>Open the browser and <code class="notranslate">POST /items:queue</code>
<ul dir="auto">
<li>For example, go to <a href="http://127.0.0.1:8000/docs#/default/queue_new_item_items_queue_post" rel="nofollow">http://127.0.0.1:8000/docs#/default/queue_new_item_items_queue_post</a></li>
<li>Click to Try it out</li>
<li>Click to Execute (default request body of <code class="notranslate">{}</code> is fine)</li>
</ul>
</li>
<li>I expect the string response "An item has been queued for later inclusion."</li>
<li>I get a 404 not found response, with JSON body {"detail": "Not Found"}</li>
</ul>
<h3 dir="auto">Operating System</h3>
<p dir="auto">macOS</p>
<h3 dir="auto">Operating System Details</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">FastAPI Version</h3>
<p dir="auto">0.78.0</p>
<h3 dir="auto">Python Version</h3>
<p dir="auto">Python 3.9.12</p>
<h3 dir="auto">Additional Context</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">First check</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I added a very descriptive title to this issue.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I used the GitHub search to find a similar issue and didn't find it.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I searched the FastAPI documentation, with the integrated search.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already searched in Google "How to X in FastAPI" and didn't find any information.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already read and followed all the tutorial in the docs and didn't find an answer.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/samuelcolvin/pydantic">Pydantic</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/swagger-api/swagger-ui">Swagger UI</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/Redocly/redoc">ReDoc</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> After submitting this, I commit to one of:
<ul dir="auto">
<li>Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.</li>
<li>I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.</li>
<li>Implement a Pull Request for a confirmed bug.</li>
</ul>
</li>
</ul>

<h3 dir="auto">Example</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
def with_transaction(func):
    @wraps(func)
    async def wrap_func(request, *args, **kwargs):
        session = request.app.state.database.get_session()
        try:
            result = await func(request, *args, **kwargs)
            session.commit()
        except Exception as e:
            session.rollback()
        finally:
            session.close()
        return result

    return wrap_func

@router.post('/users', response_model=schemas.User)
@with_transaction
def create_user(user: schemas.UserCreate, session: Session = Depends(get_session)):
    user = User(provider=user.provider)
    session.add(user)
    if user.foobar == 'force_error':
        raise Exception('my custom exception')
    return user"><pre class="notranslate"><span class="pl-k">def</span> <span class="pl-en">with_transaction</span>(<span class="pl-s1">func</span>):
    <span class="pl-en">@<span class="pl-en">wraps</span>(<span class="pl-s1">func</span>)</span>
    <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">wrap_func</span>(<span class="pl-s1">request</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>):
        <span class="pl-s1">session</span> <span class="pl-c1">=</span> <span class="pl-s1">request</span>.<span class="pl-s1">app</span>.<span class="pl-s1">state</span>.<span class="pl-s1">database</span>.<span class="pl-en">get_session</span>()
        <span class="pl-k">try</span>:
            <span class="pl-s1">result</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">func</span>(<span class="pl-s1">request</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>)
            <span class="pl-s1">session</span>.<span class="pl-en">commit</span>()
        <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">e</span>:
            <span class="pl-s1">session</span>.<span class="pl-en">rollback</span>()
        <span class="pl-k">finally</span>:
            <span class="pl-s1">session</span>.<span class="pl-en">close</span>()
        <span class="pl-k">return</span> <span class="pl-s1">result</span>

    <span class="pl-k">return</span> <span class="pl-s1">wrap_func</span>

<span class="pl-en">@<span class="pl-s1">router</span>.<span class="pl-en">post</span>(<span class="pl-s">'/users'</span>, <span class="pl-s1">response_model</span><span class="pl-c1">=</span><span class="pl-s1">schemas</span>.<span class="pl-v">User</span>)</span>
<span class="pl-en">@<span class="pl-s1">with_transaction</span></span>
<span class="pl-k">def</span> <span class="pl-en">create_user</span>(<span class="pl-s1">user</span>: <span class="pl-s1">schemas</span>.<span class="pl-v">UserCreate</span>, <span class="pl-s1">session</span>: <span class="pl-v">Session</span> <span class="pl-c1">=</span> <span class="pl-v">Depends</span>(<span class="pl-s1">get_session</span>)):
    <span class="pl-s1">user</span> <span class="pl-c1">=</span> <span class="pl-v">User</span>(<span class="pl-s1">provider</span><span class="pl-c1">=</span><span class="pl-s1">user</span>.<span class="pl-s1">provider</span>)
    <span class="pl-s1">session</span>.<span class="pl-en">add</span>(<span class="pl-s1">user</span>)
    <span class="pl-k">if</span> <span class="pl-s1">user</span>.<span class="pl-s1">foobar</span> <span class="pl-c1">==</span> <span class="pl-s">'force_error'</span>:
        <span class="pl-k">raise</span> <span class="pl-v">Exception</span>(<span class="pl-s">'my custom exception'</span>)
    <span class="pl-k">return</span> <span class="pl-s1">user</span></pre></div>
<h3 dir="auto">Description</h3>
<p dir="auto">My requirements:</p>
<ol dir="auto">
<li>commit before response validation if path operation succeeds</li>
<li>rollback if path operation fails (raises custom exception)</li>
<li>such feature is optional for each endpoint</li>
</ol>
<p dir="auto">After suffering from the limitation of dependencies, middlewares, and a lot of custom xxx, I finally decided to manage transactions on my own. (sigh, neither dependencies nor middlewares have complete control on request lifecycles)<br>
It seems decorator is the last and optimal practice.<br>
However, the sample decorator above cannot get dependencies injected...</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="TypeError: wrap_func() missing 1 required positional argument: 'request'"><pre class="notranslate"><code class="notranslate">TypeError: wrap_func() missing 1 required positional argument: 'request'
</code></pre></div>
<p dir="auto">How sad and frustrating such a great framework can't even do this simple and frequently-used feature...</p>