diff --git a/docs/img/tutorial/bigger-applications/image01.png b/docs/img/tutorial/bigger-applications/image01.png
new file mode 100644
index 000000000..78544b403
Binary files /dev/null and b/docs/img/tutorial/bigger-applications/image01.png differ
diff --git a/docs/src/bigger_applications/app/tutorial003.py b/docs/src/bigger_applications/app/main.py
similarity index 53%
rename from docs/src/bigger_applications/app/tutorial003.py
rename to docs/src/bigger_applications/app/main.py
index 4c2a348db..4e614a007 100644
--- a/docs/src/bigger_applications/app/tutorial003.py
+++ b/docs/src/bigger_applications/app/main.py
@@ -1,7 +1,7 @@
from fastapi import FastAPI
-from .routers.tutorial001 import router as users_router
-from .routers.tutorial002 import router as items_router
+from .routers.items import router as items_router
+from .routers.users import router as users_router
app = FastAPI()
diff --git a/docs/src/bigger_applications/app/routers/tutorial002.py b/docs/src/bigger_applications/app/routers/items.py
similarity index 75%
rename from docs/src/bigger_applications/app/routers/tutorial002.py
rename to docs/src/bigger_applications/app/routers/items.py
index 46a241902..2297e2d27 100644
--- a/docs/src/bigger_applications/app/routers/tutorial002.py
+++ b/docs/src/bigger_applications/app/routers/items.py
@@ -3,11 +3,11 @@ from fastapi import APIRouter
router = APIRouter()
-@router.get("/")
+@router.get("/", tags=["items"])
async def read_items():
return [{"name": "Item Foo"}, {"name": "item Bar"}]
-@router.get("/{item_id}")
+@router.get("/{item_id}", tags=["items"])
async def read_item(item_id: str):
return {"name": "Fake Specific Item", "item_id": item_id}
diff --git a/docs/src/bigger_applications/app/routers/tutorial001.py b/docs/src/bigger_applications/app/routers/users.py
similarity index 68%
rename from docs/src/bigger_applications/app/routers/tutorial001.py
rename to docs/src/bigger_applications/app/routers/users.py
index 37e3fbc4a..e88b20cd1 100644
--- a/docs/src/bigger_applications/app/routers/tutorial001.py
+++ b/docs/src/bigger_applications/app/routers/users.py
@@ -3,16 +3,16 @@ from fastapi import APIRouter
router = APIRouter()
-@router.get("/users/")
+@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Foo"}, {"username": "Bar"}]
-@router.get("/users/me")
+@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}
-@router.get("/users/{username}")
+@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}
diff --git a/docs/tutorial/bigger-applications.md b/docs/tutorial/bigger-applications.md
index 056dcbbbf..81ebb0440 100644
--- a/docs/tutorial/bigger-applications.md
+++ b/docs/tutorial/bigger-applications.md
@@ -1,13 +1,284 @@
-Coming soon...
+If you are building an application or a web API, it's rarely the case that you can put everything on a single file.
+
+**FastAPI** provides a convenience tool to structure your application while keeping all the flexibility.
+
+
+## An example file structure
+
+Let's say you have a file structure like this:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+│ └── routers
+│ ├── __init__.py
+│ ├── items.py
+│ └── users.py
+```
+
+!!! tip
+ There are two `__init__.py` files: one in each directory or subdirectory.
+
+ This is what allows importing code from one file into another.
+
+ For example, in `app/main.py` you could have a line like:
+
+ ```
+ from app.routers import items
+ ```
+
+
+* The `app` directory contains everything.
+* This `app` directory has an empty file `app/__init__.py`.
+ * So, the `app` directory is a "Python package" (a collection of "Python modules").
+* The `app` directory also has a `app/main.py` file.
+ * As it is inside a Python package directory (because there's a file `__init__.py`), it is a "module" of that package: `app.main`.
+* There's a subdirectory `app/routers/`.
+* The subdirectory `app/routers` also has an empty file `__init__.py`.
+ * So, it is a "Python subpackage".
+* The file `app/routers/items.py` is beside the `app/routers/__init__.py`.
+ * So, it's a submodule: `app.routers.items`.
+* The file `app/routers/users.py` is beside the `app/routers/__init__.py`.
+ * So, it's a submodule: `app.routers.users`.
+
+
+## `APIRouter`
+
+Let's say the file dedicated to handling just users is the submodule at `/app/routers/users.py`.
+
+You want to have the *path operations* related to your users separated from the rest of the code, to keep it organized.
+
+But it's still part of the same **FastAPI** application/web API (it's part of the same "Python Package").
+
+You can create the *path operations* for that module using `APIRouter`.
+
+
+### Import `APIRouter`
+
+You import it and create an "instance" the same way you would with the class `FastAPI`:
+
+```Python hl_lines="1 3"
+{!./src/bigger_applications/app/routers/users.py!}
+```
+
+
+### Path operations with `APIRouter`
+
+And then you use it to declare your *path operations*.
+
+Use it the same way you would use the `FastAPI` class:
+
+```Python hl_lines="6 11 16"
+{!./src/bigger_applications/app/routers/users.py!}
+```
+
+You can think of `APIRouter` as a "mini `FastAPI`" class.
+
+All the same options are supported.
+
+All the same parameters, responses, dependencies, tags, etc.
+
+!!! tip
+ In this example, the variable is called `router`, but you can name it however you want.
+
+We are going to include this `APIrouter` in the main `FastAPI` app, but first, let's add another `APIRouter`.
+
+
+## Another module with `APIRouter`
+
+Let's say you also have the endpoints dedicated to handling "Items" from your application in the module at `app/routers/items.py`.
+
+You have path operations for:
+
+* `/items/`
+* `/items/{item_id}`
+
+It's all the same structure as with `app/routers/users.py`.
+
+But let's say that this time we are more lazy.
+
+And we don't want to have to explicitly type `/items/` in every path operation, we can do it later:
+
+```Python hl_lines="6 11 16"
+{!./src/bigger_applications/app/routers/items.py!}
+```
+
+
+## The main `FastAPI`
+
+Now, let's see the module at `app/main.py`.
+
+Here's where you import and use the class `FastAPI`.
+
+This will be the main file in your application that ties everything together.
+
+### Import `FastAPI`
+
+You import and create a `FastAPI` class as normally:
+
+```Python hl_lines="1 6"
+{!./src/bigger_applications/app/main.py!}
+```
+
+### Import the `APIRouter`
+
+But this time we are not adding path operations directly with the `FastAPI` `app`.
+
+We import the `APIRouter`s from the other files:
+
+```Python hl_lines="3 4"
+{!./src/bigger_applications/app/main.py!}
+```
+
+As the file `app/routers/items.py` is part of the same Python package, we can import it using "dot notation".
+
+
+### How the importing works
+
+The section:
```Python
-{!./src/bigger_applications/app/routers/tutorial001.py!}
+from .routers.items import router
```
+Means:
+
+* Starting in the same package that this module (the file `app/main.py`) lives in (the directory `app/`)...
+* look for the subpackage `routers` (the directory at `app/routers/`)...
+* and from it, the submodule `items` (the file at `app/routers/items.py`)...
+* and from that submodule, import the variable `router`.
+
+The variable `router` is the same one we created in the file `app/routers/items.py`. It's an `APIRouter`.
+
+We could also import it like:
+
```Python
-{!./src/bigger_applications/app/routers/tutorial002.py!}
+from app.routers.items import router
```
+!!! info
+ The first version is a "relative import".
+
+ The second version is an "absolute import".
+
+ To learn more about Python Packages and Modules, read the official Python documentation about Modules.
+
+
+### Avoid name collisions
+
+We are importing a variable named `router` from the submodule `items`.
+
+But we also have another variable named `router` in the submodule `users`.
+
+If we import one after the other, like:
+
```Python
-{!./src/bigger_applications/app/tutorial003.py!}
+from .routers.items import router
+from .routers.users import router
+```
+
+The `router` from `users` will overwrite the one form `items` and we won't be able to use them at the same time.
+
+So, to be able to use both of them in the same file, we rename them while importing them using `as`:
+
+```Python hl_lines="3 4"
+{!./src/bigger_applications/app/main.py!}
+```
+
+
+### Include an `APIRouter`
+
+Now, let's include the router from the submodule `users`, now in the variable `users_router`:
+
+```Python hl_lines="8"
+{!./src/bigger_applications/app/main.py!}
+```
+
+With `app.include_router()` we can add an `APIRouter` to the main `FastAPI` application.
+
+It will include all the routes from that router as part of it.
+
+!!! note "Technical Details"
+ It will actually internally create a path operation for each path operation that was declared in the `APIRouter`.
+
+ So, behind the scenes, it will actually work as if everything was the same single app.
+
+
+!!! check
+ You don't have to worry about performance when including routers.
+
+ This will take microseconds and will only happen at startup.
+
+ So it won't affect performance.
+
+
+### Include an `APIRouter` with a prefix
+
+Now, let's include the router form the `items` submodule, now in the variable `items_router`.
+
+But, remember that we were lazy and didn't add `/items/` to all the path operations?
+
+We can add a prefix to all the path operations using the parameter `prefix` of `app.include_router()`.
+
+As the path of each path operation has to start with `/`, like in:
+
+```Python hl_lines="1"
+@router.get("/{item_id}", tags=["items"])
+async def read_item(item_id: str):
+ ...
+```
+
+...the prefix must not include a final `/`.
+
+So, the prefix in this case would be `/items`:
+
+```Python hl_lines="9"
+{!./src/bigger_applications/app/main.py!}
+```
+
+The end result is that the item paths are now:
+
+* `/items/`
+* `/items/{item_id}`
+
+...as we intended.
+
+!!! check
+ The `prefix` parameter is (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
+
+
+!!! tip
+ You could also add path operations directly, for example with: `@app.get(...)`.
+
+ Apart from `app.include_router()`, in the same **FastAPI** app.
+
+ It would still work the same.
+
+
+!!! info "Very Technical Details"
+ **Note**: this is a very technical detail that you probably can **just skip**.
+
+ ---
+
+ The `APIRouter`s are not "mounted", they are not isolated from the rest of the application.
+
+ This is because we want to include their path operations in the OpenAPI schema and the user interfaces.
+
+ As we cannot just isolate them and "mount" them independently of the rest, the path operations are "cloned" (re-created), not included directly.
+
+
+## Check the automatic API docs
+
+Now, run `uvicorn`, using the module `app.main` and the variable `app`:
+
+```bash
+uvicorn app.main:app --debug
```
+
+And open the docs at http://127.0.0.1:8000/docs.
+
+You will see the automatic API docs, including the paths from all the submodules:
+
+
diff --git a/tests/test_tutorial/test_bigger_applications/test_tutorial003.py b/tests/test_tutorial/test_bigger_applications/test_main.py
similarity index 96%
rename from tests/test_tutorial/test_bigger_applications/test_tutorial003.py
rename to tests/test_tutorial/test_bigger_applications/test_main.py
index 680bc8efe..eb68c4492 100644
--- a/tests/test_tutorial/test_bigger_applications/test_tutorial003.py
+++ b/tests/test_tutorial/test_bigger_applications/test_main.py
@@ -1,7 +1,7 @@
import pytest
from starlette.testclient import TestClient
-from bigger_applications.app.tutorial003 import app
+from bigger_applications.app.main import app
client = TestClient(app)
@@ -17,10 +17,24 @@ openapi_schema = {
"content": {"application/json": {"schema": {}}},
}
},
+ "tags": ["users"],
"summary": "Read Users Get",
"operationId": "read_users_users__get",
}
},
+ "/users/me": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ "tags": ["users"],
+ "summary": "Read User Me Get",
+ "operationId": "read_user_me_users_me_get",
+ }
+ },
"/users/{username}": {
"get": {
"responses": {
@@ -39,6 +53,7 @@ openapi_schema = {
},
},
},
+ "tags": ["users"],
"summary": "Read User Get",
"operationId": "read_user_users__username__get",
"parameters": [
@@ -51,18 +66,6 @@ openapi_schema = {
],
}
},
- "/users/me": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- }
- },
- "summary": "Read User Me Get",
- "operationId": "read_user_me_users_me_get",
- }
- },
"/items/": {
"get": {
"responses": {
@@ -71,6 +74,7 @@ openapi_schema = {
"content": {"application/json": {"schema": {}}},
}
},
+ "tags": ["items"],
"summary": "Read Items Get",
"operationId": "read_items_items__get",
}
@@ -93,6 +97,7 @@ openapi_schema = {
},
},
},
+ "tags": ["items"],
"summary": "Read Item Get",
"operationId": "read_item_items__item_id__get",
"parameters": [