diff --git a/examples/miniapps/fastapi/README.rst b/examples/miniapps/fastapi/README.rst
new file mode 100644
index 00000000..7e99ab8b
--- /dev/null
+++ b/examples/miniapps/fastapi/README.rst
@@ -0,0 +1,121 @@
+FastAPI + Dependency Injector Example
+=====================================
+
+This is an `FastAPI `_ +
+`Dependency Injector `_ example application.
+
+The example application is a REST API that searches for funny GIFs on the `Giphy `_.
+
+Run
+---
+
+Create virtual environment:
+
+.. code-block:: bash
+
+ virtualenv venv
+ . venv/bin/activate
+
+Install requirements:
+
+.. code-block:: bash
+
+ pip install -r requirements.txt
+
+To run the application do:
+
+.. code-block:: bash
+
+ export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
+ uvicorn giphynavigator.application:app --reload
+
+The output should be something like:
+
+.. code-block::
+
+ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+ INFO: Started reloader process [4795] using watchgod
+ INFO: Started server process [4797]
+ INFO: Waiting for application startup.
+ INFO: Application startup complete.
+
+After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``,
+etc). You should see something like:
+
+.. code-block:: json
+
+ {
+ "query": "Dependency Injector",
+ "limit": 10,
+ "gifs": [
+ {
+ "url": "https://giphy.com/gifs/boxes-dependent-swbf2-6Eo7KzABxgJMY"
+ },
+ {
+ "url": "https://giphy.com/gifs/depends-J56qCcOhk6hKE"
+ },
+ {
+ "url": "https://giphy.com/gifs/web-series-ccstudios-bro-dependent-1lhU8KAVwmVVu"
+ },
+ {
+ "url": "https://giphy.com/gifs/TheBoysTV-friends-friend-weneedeachother-XxR9qcIwcf5Jq404Sx"
+ },
+ {
+ "url": "https://giphy.com/gifs/netflix-a-series-of-unfortunate-events-asoue-9rgeQXbwoK53pcxn7f"
+ },
+ {
+ "url": "https://giphy.com/gifs/black-and-white-sad-skins-Hs4YzLs2zJuLu"
+ },
+ {
+ "url": "https://giphy.com/gifs/always-there-for-you-i-am-here-PlayjhCco9jHBYrd9w"
+ },
+ {
+ "url": "https://giphy.com/gifs/stream-famous-dollar-YT2dvOByEwXCdoYiA1"
+ },
+ {
+ "url": "https://giphy.com/gifs/i-love-you-there-for-am-1BhGzgpZXYWwWMAGB1"
+ },
+ {
+ "url": "https://giphy.com/gifs/life-like-twerk-9hlnWxjHqmH28"
+ }
+ ]
+ }
+
+.. note::
+
+ To create your own Giphy API key follow this
+ `guide `_.
+
+Test
+----
+
+This application comes with the unit tests.
+
+To run the tests do:
+
+.. code-block:: bash
+
+ py.test giphynavigator/tests.py --cov=giphynavigator
+
+The output should be something like:
+
+.. code-block::
+
+ platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
+ plugins: cov-2.10.0, asyncio-0.14.0
+ collected 3 items
+
+ giphynavigator/tests.py ... [100%]
+
+ ---------- coverage: platform darwin, python 3.8.3-final-0 -----------
+ Name Stmts Miss Cover
+ ---------------------------------------------------
+ giphynavigator/__init__.py 0 0 100%
+ giphynavigator/application.py 13 0 100%
+ giphynavigator/containers.py 6 0 100%
+ giphynavigator/endpoints.py 5 0 100%
+ giphynavigator/giphy.py 14 9 36%
+ giphynavigator/services.py 9 1 89%
+ giphynavigator/tests.py 38 0 100%
+ ---------------------------------------------------
+ TOTAL 85 10 88%
diff --git a/examples/miniapps/fastapi/config.yml b/examples/miniapps/fastapi/config.yml
new file mode 100644
index 00000000..d1276f8c
--- /dev/null
+++ b/examples/miniapps/fastapi/config.yml
@@ -0,0 +1,5 @@
+giphy:
+ request_timeout: 10
+default:
+ query: "Dependency Injector"
+ limit: 10
diff --git a/examples/miniapps/fastapi/giphynavigator/__init__.py b/examples/miniapps/fastapi/giphynavigator/__init__.py
new file mode 100644
index 00000000..1c744ca5
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/__init__.py
@@ -0,0 +1 @@
+"""Top-level package."""
diff --git a/examples/miniapps/fastapi/giphynavigator/application.py b/examples/miniapps/fastapi/giphynavigator/application.py
new file mode 100644
index 00000000..805f4868
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/application.py
@@ -0,0 +1,21 @@
+"""Application module."""
+
+from fastapi import FastAPI
+
+from .containers import Container
+from . import endpoints
+
+
+def create_app() -> FastAPI:
+ container = Container()
+ container.config.from_yaml('config.yml')
+ container.config.giphy.api_key.from_env('GIPHY_API_KEY')
+ container.wire(modules=[endpoints])
+
+ app = FastAPI()
+ app.container = container
+ app.add_api_route('/', endpoints.index)
+ return app
+
+
+app = create_app()
diff --git a/examples/miniapps/fastapi/giphynavigator/containers.py b/examples/miniapps/fastapi/giphynavigator/containers.py
new file mode 100644
index 00000000..730c162e
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/containers.py
@@ -0,0 +1,21 @@
+"""Containers module."""
+
+from dependency_injector import containers, providers
+
+from . import giphy, services
+
+
+class Container(containers.DeclarativeContainer):
+
+ config = providers.Configuration()
+
+ giphy_client = providers.Factory(
+ giphy.GiphyClient,
+ api_key=config.giphy.api_key,
+ timeout=config.giphy.request_timeout,
+ )
+
+ search_service = providers.Factory(
+ services.SearchService,
+ giphy_client=giphy_client,
+ )
diff --git a/examples/miniapps/fastapi/giphynavigator/endpoints.py b/examples/miniapps/fastapi/giphynavigator/endpoints.py
new file mode 100644
index 00000000..e1a37716
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/endpoints.py
@@ -0,0 +1,18 @@
+"""Endpoints module."""
+
+from dependency_injector.wiring import Provide
+
+from .containers import Container
+
+
+async def index(
+ query: str = Provide[Container.config.default.query],
+ limit: int = Provide[Container.config.default.limit.as_int()],
+ search_service = Provide[Container.search_service],
+):
+ gifs = await search_service.search(query, limit)
+ return {
+ 'query': query,
+ 'limit': limit,
+ 'gifs': gifs,
+ }
diff --git a/examples/miniapps/fastapi/giphynavigator/giphy.py b/examples/miniapps/fastapi/giphynavigator/giphy.py
new file mode 100644
index 00000000..22a5f6a4
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/giphy.py
@@ -0,0 +1,26 @@
+"""Giphy client module."""
+
+from aiohttp import ClientSession, ClientTimeout
+
+
+class GiphyClient:
+
+ API_URL = 'https://api.giphy.com/v1'
+
+ def __init__(self, api_key, timeout):
+ self._api_key = api_key
+ self._timeout = ClientTimeout(timeout)
+
+ async def search(self, query, limit):
+ """Make search API call and return result."""
+ url = f'{self.API_URL}/gifs/search'
+ params = {
+ 'q': query,
+ 'api_key': self._api_key,
+ 'limit': limit,
+ }
+ async with ClientSession(timeout=self._timeout) as session:
+ async with session.get(url, params=params) as response:
+ if response.status != 200:
+ response.raise_for_status()
+ return await response.json()
diff --git a/examples/miniapps/fastapi/giphynavigator/services.py b/examples/miniapps/fastapi/giphynavigator/services.py
new file mode 100644
index 00000000..1c86e0d7
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/services.py
@@ -0,0 +1,18 @@
+"""Services module."""
+
+from .giphy import GiphyClient
+
+
+class SearchService:
+
+ def __init__(self, giphy_client: GiphyClient):
+ self._giphy_client = giphy_client
+
+ async def search(self, query, limit):
+ """Search for gifs and return formatted data."""
+ if not query:
+ return []
+
+ result = await self._giphy_client.search(query, limit)
+
+ return [{'url': gif['url']} for gif in result['data']]
diff --git a/examples/miniapps/fastapi/giphynavigator/tests.py b/examples/miniapps/fastapi/giphynavigator/tests.py
new file mode 100644
index 00000000..4ae91493
--- /dev/null
+++ b/examples/miniapps/fastapi/giphynavigator/tests.py
@@ -0,0 +1,78 @@
+"""Tests module."""
+
+from unittest import mock
+
+import pytest
+from httpx import AsyncClient
+
+from giphynavigator.application import app
+from giphynavigator.giphy import GiphyClient
+
+
+@pytest.fixture
+def client(event_loop):
+ client = AsyncClient(app=app, base_url='http://test')
+ yield client
+ event_loop.run_until_complete(client.aclose())
+
+
+@pytest.mark.asyncio
+async def test_index(client):
+ giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
+ giphy_client_mock.search.return_value = {
+ 'data': [
+ {'url': 'https://giphy.com/gif1.gif'},
+ {'url': 'https://giphy.com/gif2.gif'},
+ ],
+ }
+
+ with app.container.giphy_client.override(giphy_client_mock):
+ response = await client.get(
+ '/',
+ params={
+ 'query': 'test',
+ 'limit': 10,
+ },
+ )
+
+ assert response.status_code == 200
+ data = response.json()
+ assert data == {
+ 'query': 'test',
+ 'limit': 10,
+ 'gifs': [
+ {'url': 'https://giphy.com/gif1.gif'},
+ {'url': 'https://giphy.com/gif2.gif'},
+ ],
+ }
+
+
+@pytest.mark.asyncio
+async def test_index_no_data(client):
+ giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
+ giphy_client_mock.search.return_value = {
+ 'data': [],
+ }
+
+ with app.container.giphy_client.override(giphy_client_mock):
+ response = await client.get('/')
+
+ assert response.status_code == 200
+ data = response.json()
+ assert data['gifs'] == []
+
+
+@pytest.mark.asyncio
+async def test_index_default_params(client):
+ giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
+ giphy_client_mock.search.return_value = {
+ 'data': [],
+ }
+
+ with app.container.giphy_client.override(giphy_client_mock):
+ response = await client.get('/')
+
+ assert response.status_code == 200
+ data = response.json()
+ assert data['query'] == app.container.config.default.query()
+ assert data['limit'] == app.container.config.default.limit()
diff --git a/examples/miniapps/fastapi/requirements.txt b/examples/miniapps/fastapi/requirements.txt
new file mode 100644
index 00000000..6eddac10
--- /dev/null
+++ b/examples/miniapps/fastapi/requirements.txt
@@ -0,0 +1,8 @@
+dependency-injector
+fastapi
+uvicorn
+aiohttp
+httpx
+pyyaml
+pytest-asyncio
+pytest-cov