# Web development with Python FastAPI
{id: fastapi}

## Install FastAPI
{id: fastapi-install}

* [FastAPI](https://fastapi.tiangolo.com/) to build web application back-ends that serve JSON or other data formats.

* Python 3.8 or newer is needed

* In order to get started one needs to install FastAPI manually, or, as we can see on the next page add it as part of the `requirements.txt` file.

```
pip install "fastapi[all]"
```

## FastAPI -  Hello World
{id: fastapi-hello-world}
{i: FastAPI}
{i: get}
{i: virtualenv}

* The requirements include both `FastAPI` and `pytest` so we can write and run tests for our web application.

![](examples/fastapi/hello-world/requirements.txt)


* It is recommended to set up a virtual environment or use some other way to separate environments.

```
virtualenv -p python3 venv
source venv/bin/activate
pip install -r requirements.txt
```

* We need a single file in which we `import` the `FastAPI` class.
* We then create an instance of it. We can call it any name, but most of the examples are using `app` so we'll use that as well.
* For each URL path we need to create a mapping to indicate which function needs to be executed when someone access that path.
* The functions are defined as `async` so FastAPI can handle several requests at the same time. The name of the function does not matter.
* The decorator `@app.get("/")` is doing the mapping.
* The function needs to return some Python data structure that will be converted to JSON when it is sent back to the client.


![](examples/fastapi/hello-world/main.py)

* In order to see this working launch the development web server that comes with the installation of FastAPI.

```
fastapi dev main.py
```

Then visit `http://localhost:8000/`

You can also visit some other pages on this site:

* `http://localhost:8000/docs` to see the documentation generated by [Swagger UI](https://swagger.io/tools/swagger-ui/)
* `http://localhost:8000/redoc` to see the documentation generated by [Redoc](https://redocly.com/)
* `http://localhost:8000/openapi.json`

* path - endpoint - route


## FastAPI -  Test Hello World
{id: fastapi-hello-world-test}
{i: TestClient}
{i: assert}

Writing the web application is nice, but we better also write tests that verify the application works properly.
This will make it easier to verify that none of the changes we introduce later on will break parts that have been
working and tested already.

The key here is to have a file that starts with `test_` that has a function name that starts with `test_` that uses `assert` to check values.

![](examples/fastapi/hello-world/test_main.py)

Just run `pytest` to execute the tests.

```
pytest
```


## FastAPI with Docker compose
{id: fastapi-with-docker-compose}

![](examples/fastapi/Dockerfile)

![](examples/fastapi/docker-compose.yml)

![](examples/fastapi/requirements.txt)

```
docker compose up
```

```
docker exec -it fastapi_app_1 bash
```

```
uvicorn main:app --reload --host=0.0.0.0
```


## FastAPI - Dynamic response
{id: fastapi-dynamic-response}
{i: datetime}

* A small step ahead generating part of the content of the response dynamically.

![](examples/fastapi/dynamic-response/main.py)


## FastAPI - Echo GET - Query Parameters
{id: fastapi-echo-get}

![](examples/fastapi/echo-get/main.py)

```
http://localhost:8000/?text=Foo%20Bar
```

## FastAPI - Echo GET - Query Parameters - test
{id: fastapi-test-echo-get}

![](examples/fastapi/echo-get/test_main.py)


## FastAPI - Echo POST - request body
{id: fastapi-echo-post}

![](examples/fastapi/echo-post/main.py)

```
http://localhost:8000/?text=Foo%20Bar
```

```
curl -d '{"text":"Foo Bar"}' -H "Content-Type: application/json" -X POST http://localhost:8000/
```

![](examples/fastapi/echo-post/client.py)


## FastAPI - Echo POST - request body - test
{id: fastapi-test-echo-post}

![](examples/fastapi/echo-post/test_main.py)

## FastAPI - Calculator GET
{id: fastapi-calculator-get}

![](examples/fastapi/calculator-get/main.py)

```
http://localhost:8000/?a=2&b=3
```

## FastAPI - Calculator GET - Test
{id: fastapi-test-calculator-get}

![](examples/fastapi/calculator-get/test_main.py)

## FastAPI - Path Parameters - str
{id: fastapi-path-parameters-str}

![](examples/fastapi/username-path/main.py)

```
http://localhost:8000/user/foobar
http://localhost:8000/user/foo bar


http://localhost:8000/user/ - 404
```

## FastAPI - Path Parameters - str - test
{id: fastapi-test-path-parameters-str}

![](examples/fastapi/username-path/test_main.py)

## FastAPI - Path Parameters - int
{id: fastapi-path-parameters-int}

![](examples/fastapi/userid-path/main.py)

```
http://localhost:8000/user/23      works
http://localhost:8000/user/foo     422 error
http://localhost:8000/user/2.3     422 error
```

## FastAPI - Path Parameters - int - test
{id: fastapi-test-path-parameters-int}

![](examples/fastapi/userid-path/test_main.py)

## FastAPI - Path Parameters - specific values with enum
{id: fastapi-path-parameters-specific-values-with-enum}

![](examples/fastapi/specific-values-path/main.py)

```
http://localhost:8000/car/Volvo

http://localhost:8000/car/volvo     error
```

## FastAPI - Path Parameters - specific values with enum - test
{id: fastapi-test-path-parameters-specific-values-with-enum}

![](examples/fastapi/specific-values-path/test_main.py)


## FastAPI - Path containing a directory path
{id: fastapi-path-containing-a-path}

![](examples/fastapi/path-of-path/main.py)

```
http://localhost:8000/shallow/a.txt    works
http://localhost:8000/shallow/a/b.txt  not found

http://localhost:8000/deep/a.txt       works
http://localhost:8000/deep/a/b.txt     works
```

## FastAPI - Path containing a directory path - test
{id: fastapi-test-path-containing-a-path}

![](examples/fastapi/path-of-path/test_main.py)

## Return main HTML page
{id: fastapi-return-html-page}

![](examples/fastapi/return-html/main.py)

## Return main HTML page - test
{id: fastapi-test-return-html-page}

![](examples/fastapi/return-html/test_main.py)


## Return main HTML file
{id: fastapi-return-html-file}

![](examples/fastapi/return-html-file/main.py)
![](examples/fastapi/return-html-file/index.html)


## Send 400 error
{id: fastapi-send-400-error}

* user/{id}  but we don't have that specific id.
* abort(400) with some status code

![](examples/fastapi/send-400-error/main.py)

## Send 400 error - test
{id: fastapi-test-send-400-error}

![](examples/fastapi/send-400-error/test_main.py)


## FastAPI - in memory counter
{id: fastapi-in-memory-counter}

![](examples/fastapi/counter-in-memory/main.py)

## FastAPI - on disk counter
{id: fastapi-on-disk-counter}

![](examples/fastapi/counter-on-disk/main.py)

## FastAPI - on disk multi-counter uising JSON
{id: fastapi-on-disk-multi-counter-using-json}

![](examples/fastapi/counter-on-disk-json/main.py)

## FastAPI - get header from request
{id: fastapi-get-header-from-request}

![](examples/fastapi/get-header/main.py)

![](examples/fastapi/get-header/test_main.py)


## FastAPI - set arbitrary header in response
{id: fastapi-set-header-in-response}

![](examples/fastapi/set-header/main.py)

![](examples/fastapi/set-header/test_main.py)

## FastAPI - serve static files - JavaScript example
{id: fastapi-static-files-javascript}

![](examples/fastapi/static-files/main.py)

![](examples/fastapi/static-files/js/demo.js)

![](examples/fastapi/static-files/index.html)


## FastAPI mounted sub-applications
{id: fastapi-mounted-sub-applications}


![](examples/fastapi/mounted-applications/main.py)

![](examples/fastapi/mounted-applications/api_v1.py)

```
uvicorn main:api_v1 --reload --host=0.0.0.0
uvicorn main:main --reload --host=0.0.0.0
```

## FastAPI - todo
{id: fastapi-todo}

* Access to MongoDB
* Access to PostgreSQL
* Access to SQLite
* Session ???
* Can we have the query parameters and the internal variables be different (e.g.   paramName  and param_name ?)
* handel exceptions (500 errors)

```
pip install "uvicorn[standard]"
```

```
uvicorn main:app --reload
```


