Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "fastapi-app",
"dockerComposeFile": ["../docker-compose.yml"],
"service": "api",
"workspaceFolder": "/app",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.flake8",
"ms-python.black-formatter",
"ms-vscode-remote.remote-containers"
],
"settings": {
"files.encoding": "utf8",
"files.eol": "\n",
"editor.formatOnSave": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"flake8.args": ["--max-line-length=88", "--ignore=E203,W503,W504"]
}
}
},
"forwardPorts": [8000]
}
7 changes: 0 additions & 7 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
## Description

-

## Related Issues

<!--
Link to the issue that is fixed by this PR (if there is one)
e.g. Fixes #1234, Addresses #1234, Related to #1234, etc.
-->
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"recommendations": [
"ms-python.python",
"ms-python.flake8",
"ms-python.black-formatter"
"ms-python.black-formatter",
"ms-vscode-remote.remote-containers"
]
}
100 changes: 44 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,33 @@

---

## Setup development environment
## Setup development environment (Docker compose)

Please install `Docker` and `Docker compose` first.
Please install [`Docker` and `Docker compose`](https://www.docker.com/) first.

https://www.docker.com/
## Manual setup

After installation, run the following command to create a local Docker container.

```bash
docker-compose build
docker-compose up -d
```

If you want to check the log while Docker container is running, then try to use following command:

```bash
```sh
docker-compose up
```

If Docker is running successfully, the API and DB server will be launched as shown in the following:

- API server: http://localhost:8000
- API Docs: http://localhost:8000/v1/docs
- DB server: http://localhost:3306

_Be careful, it won't work if the port is occupied by another application._

If you want to check docker is actually working, then you can check it with following command:

```bash
docker ps
```

If you want to go inside of docker container, then try to use following command:

```bash
docker-compose exec mysql bash
docker-compose exec api bash
```

For shutdown of the docker instance, please use following command:

```bash
docker-compose down
```

## Need a front-end app?
## Setup with the VS Code Dev Containers extension

If you need a front-end app for this server-side & DB server.
The above setup can be used for development, but you can also setup dev env with using the [VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).

You can clone the front-end template from:

- https://github.com/qlawmarq/nuxt3-tailwind-auth-app
- Install VS code and the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
- Run the `Dev Containers: Open Folder in Container...` command from the Command Palette or quick actions Status bar item, and select the project folder.
- Wait until the building of the application is finished, then access the application url

---

Expand All @@ -69,29 +44,39 @@ You can clone the front-end template from:

If you're [VS Code](https://code.visualstudio.com/) user, you can easily setup Python code formatter (black) and linter (flake8) by simply installing the extensions.

Automatic formatting settings have already been defined here:

`.vscode/settings.json`
Automatic formatting settings have already been defined [`.vscode/settings.json`](./.vscode/settings.json).

Just install following:

- [Black Formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter)
- [Flake8](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8)

If you are using the Dev Container, this configuration is already done in [the Dev Container settings](./.devcontainer/devcontainer.json), so you can skip it.

### How to check the DB tables in container

You can check the DB data by actually executing a query using the following command:
Use following command to go inside of docker container:

```sh
docker-compose exec mysql sh
```

Then use `mysql` command to execute a query:

```bash
docker-compose exec mysql bash
```sh
mysql -u root -p
mysql> USE fastapi_app;
mysql> SHOW TABLES;
mysql> SELECT * FROM user;
```

Your initial MySQL password is defined in `mysql/local.env`.

### How to add a library

You may want to add libraries such as requests, in which case follow these steps:
Python libraries used in this app are defined in `api/requirements.txt`.

Also you may want to add libraries such as requests, in which case follow these steps:

- Add the library to requirements.txt

Expand All @@ -103,23 +88,15 @@ requests==2.30.0

Then try a re-build and see.

```
```sh
docker-compose down
docker-compose build
docker-compose up
```

### Python library packages

Some of the Python packages used in this app are defined in `api/requirements.txt`.
Also you can add other packages there.

### Environment variable

Some of environment variable, like a database name and user is defined in `docker-compose.yml`.
You can customize it as you like.

If you will use docker, then please define your environment variable to `docker-compose.yml`.
However, you will NOT use docker, then please create `.env` file for your API server.
Some of environment variable, like a database name and user is defined in `docker-compose.yml` or `Dockerfile`.

### DB Migrations

Expand All @@ -130,10 +107,21 @@ The sample table definition has already been created with the name `create_user_

### Save the local DB changes as a dump file

```bash
If you need to share local DB changes with other developers, you can use `mysqldump` to create a backup and share it with them.

To create a `dump.sql', run the following command:

```sh
docker-compose exec database mysqldump -u root -p fastapi_app > mysql/db/dump.sql
```

### API documentation
Then, to reinitialize the DB, remove the named volumes declared in the "volumes" section of the Compose file.

https://docs.docker.com/engine/reference/commandline/compose_down/

```sh
docker-compose down -v
```

http://localhost:8000/redoc
Then, run `docker-compose up` to launch the development environment.
And confirm that your DB changes are reflected.
1 change: 0 additions & 1 deletion api/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from .provider import *
5 changes: 3 additions & 2 deletions api/auth/controllers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import HTTPException, status
from database.query import query_put
from database.connector import DatabaseConnector
from auth.provider import AuthProvider
from auth.models import SignUpRequestModel
from user.controllers import get_users_by_email
Expand All @@ -8,13 +8,14 @@


def register_user(user_model: SignUpRequestModel):
database = DatabaseConnector()
user = get_users_by_email(user_model.email)
if len(user) != 0:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Email already exists."
)
hashed_password = auth_handler.get_password_hash(user_model.password)
query_put(
database.query_put(
"""
INSERT INTO user (
first_name,
Expand Down
6 changes: 4 additions & 2 deletions api/auth/provider.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from datetime import datetime, timedelta
from typing import Annotated
from database import query_get
from database.connector import DatabaseConnector
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
import os

db_connector = DatabaseConnector()

OAUTH2_SCHEME = OAuth2PasswordBearer(tokenUrl="token")

CREDENTIALS_EXCEPTION = HTTPException(
Expand Down Expand Up @@ -122,7 +124,7 @@ async def get_current_user(
return user

def get_user_by_email(self, user_email: str) -> AuthUser:
user = query_get(
user = db_connector.query_get(
"""
SELECT
user.id,
Expand Down
6 changes: 3 additions & 3 deletions api/auth/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
auth_handler = AuthProvider()


@router.post("/v1/signup", response_model=UserAuthResponseModel)
@router.post("/v1/auth/signup", response_model=UserAuthResponseModel)
def signup_api(user_details: SignUpRequestModel):
"""
This sign-up API allow you to register your account, and return access token.
Expand All @@ -36,7 +36,7 @@ def signup_api(user_details: SignUpRequestModel):
)


@router.post("/v1/signin", response_model=UserAuthResponseModel)
@router.post("/v1/auth/signin", response_model=UserAuthResponseModel)
def signin_api(user_details: SignInRequestModel):
"""
This sign-in API allow you to obtain your access token.
Expand All @@ -55,7 +55,7 @@ def signin_api(user_details: SignInRequestModel):
)


@router.post("/v1/refresh-token", response_model=AccessTokenResponseModel)
@router.post("/v1/auth/refresh-token", response_model=AccessTokenResponseModel)
def refresh_token_api(refresh_token: str):
"""
This refresh-token API allow you to obtain new access token.
Expand Down
2 changes: 0 additions & 2 deletions api/database/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
# database module
from .query import query_get, query_put, query_update
64 changes: 64 additions & 0 deletions api/database/connector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from fastapi import HTTPException, status
import os
import pymysql.cursors
from pymysql import converters


class DatabaseConnector:
def __init__(self):
self.host = os.getenv("DATABASE_HOST")
self.user = os.getenv("DATABASE_USERNAME")
self.password = os.getenv("DATABASE_PASSWORD")
self.database = os.getenv("DATABASE")
self.port = int(os.getenv("DATABASE_PORT"))
self.conversions = converters.conversions
self.conversions[pymysql.FIELD_TYPE.BIT] = (
lambda x: False if x == b"\x00" else True
)
if not self.host:
raise EnvironmentError("DATABASE_HOST environment variable not found")
if not self.user:
raise EnvironmentError("DATABASE_USERNAME environment variable not found")
if not self.password:
raise EnvironmentError("DATABASE_PASSWORD environment variable not found")
if not self.database:
raise EnvironmentError("DATABASE environment variable not found")

def get_connection(self):
connection = pymysql.connect(
host=self.host,
port=self.port,
user=self.user,
password=self.password,
database=self.database,
cursorclass=pymysql.cursors.DictCursor,
conv=self.conversions,
)
return connection

def query_get(self, sql, param):
try:
connection = self.get_connection()
with connection:
with connection.cursor() as cursor:
cursor.execute(sql, param)
return cursor.fetchall()
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Database error: " + str(e),
)

def query_put(self, sql, param):
try:
connection = self.get_connection()
with connection:
with connection.cursor() as cursor:
cursor.execute(sql, param)
connection.commit()
return cursor.lastrowid
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Database error: " + str(e),
)
Loading