]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:sparkles: Add mermaid.js support in Markdown fenced blocks for diagrams (#985)
authorSebastián Ramírez <tiangolo@gmail.com>
Sun, 16 Feb 2020 18:48:20 +0000 (19:48 +0100)
committerGitHub <noreply@github.com>
Sun, 16 Feb 2020 18:48:20 +0000 (19:48 +0100)
docs/tutorial/dependencies/dependencies-with-yield.md
docs/tutorial/dependencies/index.md
docs/tutorial/dependencies/sub-dependencies.md
docs/tutorial/sql-databases.md
mkdocs.yml

index 31c77a589540b15279a35515f9b7ab9a7d18917a..08e9156a2b49dc6ab9145be9cfab853d6c3081f6 100644 (file)
@@ -102,6 +102,74 @@ You can have any combinations of dependencies that you want.
 
     **FastAPI** uses them internally to achieve this.
 
+## Dependencies with `yield` and `HTTPException`
+
+You saw that you can use dependencies with `yield` and have `try` blocks that catch exceptions.
+
+It might be tempting to raise an `HTTPException` or similar in the exit code, after the `yield`. But **it won't work**.
+
+The exit code in dependencies with `yield` is executed *after* [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. There's nothing catching exceptions thrown by your dependencies in the exit code (after the `yield`).
+
+So, if you raise an `HTTPException` after the `yield`, the default (or any custom) exception handler that catches `HTTPException`s and returns an HTTP 400 response won't be there to catch that exception anymore.
+
+This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks.
+
+Background tasks are run *after* the response has been sent. So there's no way to raise an `HTTPException` because there's not even a way to change the response that is *already sent*.
+
+But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with `yield`, and maybe log the error or report it to a remote tracking system.
+
+If you have some code that you know could raise an exception, do the most normal/"Pythonic" thing and add a `try` block in that section of the code.
+
+If you have custom exceptions that you would like to handle *before* returning the response and possibly modifying the response, maybe even raising an `HTTPException`, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
+
+!!! tip
+    You can still raise exceptions including `HTTPException` *before* the `yield`. But not after.
+
+The sequence of execution is more or less like this:
+
+```mermaid
+sequenceDiagram
+
+participant client as Client
+participant handler as Exception handler
+participant dep as Dep with yield
+participant operation as Path Operation
+participant tasks as Background tasks
+
+    Note over client,tasks: Can raise exception for dependency, handled after response is sent
+    Note over client,operation: Can raise HTTPException and can change the response
+    client ->> dep: Start request
+    Note over dep: Code up to yield
+    opt raise
+        dep -->> handler: Raise HTTPException
+        handler -->> client: HTTP error response
+        dep -->> dep: Raise other exception
+    end
+    dep ->> operation: Run dependency, e.g. DB session
+    opt raise
+        operation -->> handler: Raise HTTPException
+        handler -->> client: HTTP error response
+        operation -->> dep: Raise other exception
+    end
+    operation ->> client: Return response to client
+    Note over client,operation: Response is already sent, can't change it anymore
+    opt Tasks
+        operation -->> tasks: Send background tasks
+    end
+    opt Raise other exception
+        tasks -->> dep: Raise other exception
+    end
+    Note over dep: After yield
+    opt Handle other exception
+        dep -->> dep: Handle exception, can't change response. E.g. close DB session.
+    end
+```
+
+!!! tip
+    This diagram shows `HTTPException`, but you could also raise any other exception that you create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} for. And that exception would be handled by that custom exception handler instead of the dependency exit code.
+
+    But if you raise an exception that is not handled by the exception handlers, it will be handled by the exit code of the dependency.
+
 ## Context Managers
 
 ### What are "Context Managers"
index f9cb4dd4a7b3627028a8bf95ef8c2f504d33612d..c92dcebf6d7b27687eca361ea9df76ac80319797 100644 (file)
@@ -82,6 +82,19 @@ Whenever a new request arrives, **FastAPI** will take care of:
 * Get the result from your function.
 * Assign that result to the parameter in your *path operation function*.
 
+```mermaid
+graph TB
+
+common_parameters(["common_parameters"])
+read_items["/items/"]
+read_users["/users/"]
+
+common_parameters --> read_items
+common_parameters --> read_users
+```
+
+This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*.
+
 !!! check
     Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
 
@@ -154,7 +167,39 @@ Although the hierarchical dependency injection system is very simple to define a
 
 You can define dependencies that in turn can define dependencies themselves.
 
-In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and your dependencies) and providing (injecting) the results at each step.
+In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.
+
+For example, let's say you have 4 API endpoints (*path operations*):
+
+* `/items/public/`
+* `/items/private/`
+* `/users/{user_id}/activate`
+* `/items/pro/`
+
+then you could add different permission requirements for each of them just with dependencies and sub-dependencies:
+
+```mermaid
+graph TB
+
+current_user(["current_user"])
+active_user(["active_user"])
+admin_user(["admin_user"])
+paying_user(["paying_user"])
+
+public["/items/public/"]
+private["/items/private/"]
+activate_user["/users/{user_id}/activate"]
+pro_items["/items/pro/"]
+
+current_user --> active_user
+active_user --> admin_user
+active_user --> paying_user
+
+current_user --> public
+active_user --> private
+admin_user --> activate_user
+paying_user --> pro_items
+```
 
 ## Integrated with **OpenAPI**
 
index a0ea0273eb255df7df2a8b6ed823082fd70b343a..3407733808cce1cde7bd6607144d9baefd420d5a 100644 (file)
@@ -44,6 +44,17 @@ Then we can use the dependency with:
 
     But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
 
+```mermaid
+graph TB
+
+query_extractor(["query_extractor"])
+query_or_cookie_extractor(["query_or_cookie_extractor"])
+
+read_query["/items/"]
+
+query_extractor --> query_or_cookie_extractor --> read_query
+```
+
 ## Using the same dependency multiple times
 
 If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
index ab2633279fd55b59f053b4a296db482957a66668..44c06eb2440896dd7eeb0c4dd13aaee803cf1676 100644 (file)
@@ -469,6 +469,8 @@ Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in
     
     This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request.
 
+    But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank}
+
 And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy.
 
 This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
index e38ed9b7f1f3e71c68c85489dc3ed7d17fdf304b..88cd1f6ca5877b84470785e5b65e711a71036266 100644 (file)
@@ -117,6 +117,11 @@ markdown_extensions:
     - admonition
     - codehilite
     - extra
+    - pymdownx.superfences:
+        custom_fences:
+            - name: mermaid
+              class: mermaid
+              format: !!python/name:pymdownx.superfences.fence_div_format
 
 extra:
     social:
@@ -137,4 +142,5 @@ extra_css:
     - 'css/custom.css'
 
 extra_javascript:
+    - 'https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js'
     - 'js/custom.js'