Skip to content

Commit 849925e

Browse files
committed
✨ Add (module): FastAPI basic structure as RESTful API
1 parent d778131 commit 849925e

21 files changed

+1463
-73
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ repos:
2929
- --keep-runtime-typing
3030

3131
- repo: https://github.com/astral-sh/ruff-pre-commit
32-
rev: v0.4.2
32+
rev: v0.4.3
3333
hooks:
3434
# Run the linter.
3535
- id: ruff
@@ -51,7 +51,7 @@ repos:
5151
args: [ "--config", "pyproject.toml" ]
5252

5353
- repo: https://github.com/astral-sh/ruff-pre-commit
54-
rev: v0.4.2
54+
rev: v0.4.3
5555
hooks:
5656
# Run the formatter.
5757
- id: ruff-format

app/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Package app initialization.
3+
"""

app/api/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Package app.api initialization.
3+
"""

app/api/api_v1/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Package app.api.api v1 initialization.
3+
"""

app/api/api_v1/api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""
2+
Centralized API routing script.
3+
This module integrates the individual routers from the different
4+
modules of the API.
5+
"""
6+
7+
from fastapi import APIRouter
8+
9+
from app.api.api_v1.router import user
10+
11+
api_router: APIRouter = APIRouter()
12+
api_router.include_router(user.router)

app/api/api_v1/router/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Package app.api.api v1.router initialization.
3+
"""

app/api/api_v1/router/user.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""
2+
User API Router
3+
This module provides CRUD (Create, Retrieve, Update, Delete) operations
4+
for users.
5+
"""
6+
7+
from fastapi import APIRouter
8+
9+
router: APIRouter = APIRouter(prefix="/user", tags=["user"])

app/config/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Package app.config initialization.
3+
"""

app/config/init_settings.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
"""
2+
A module for base settings in the app.core.config package.
3+
"""
4+
5+
import base64
6+
from datetime import date
7+
from functools import lru_cache
8+
from pathlib import Path
9+
10+
from fastapi.openapi.models import Example
11+
from pydantic_extra_types.phone_numbers import PhoneNumber
12+
from pydantic_settings import BaseSettings, SettingsConfigDict
13+
14+
15+
def get_image_b64(image_path: str) -> str:
16+
"""
17+
Converts an image to base64 format
18+
:param image_path: Path to the image file
19+
:type image_path: str
20+
:return: The image file in base64 format
21+
:rtype: str
22+
"""
23+
return base64.b64encode(Path(image_path).read_bytes()).decode("utf")
24+
25+
26+
img_b64: str = get_image_b64("assets/images/project.png")
27+
users_b64: str = get_image_b64("assets/images/users.png")
28+
29+
30+
class InitSettings(BaseSettings):
31+
"""
32+
Init Settings class based on Pydantic Base Settings
33+
"""
34+
35+
model_config = SettingsConfigDict(
36+
case_sensitive=True,
37+
extra="allow",
38+
)
39+
40+
SALUTE: str = "Salute!"
41+
ROOT_MSG: str = "Hello, World!"
42+
SERVER_NAME: str = "FastAPI Server"
43+
PROJECT_NAME: str = "pydantic-sqlalchemy-tutorial"
44+
VERSION: str = "1.0"
45+
ENCODING: str = "UTF-8"
46+
OPENAPI_FILE_PATH: str = "/openapi.json"
47+
DATE_FORMAT: str = "%Y-%m-%d"
48+
DATETIME_FORMAT: str = "%Y-%m-%d %H:%M:%S"
49+
FILE_DATE_FORMAT: str = "%d-%b-%Y-%H-%M-%S"
50+
IMAGES_APP: str = "images"
51+
IMAGES_PATH: str = "/assets/images"
52+
IMAGES_DIRECTORY: str = "assets/images"
53+
LOG_FORMAT: str = (
54+
"[%(name)s][%(asctime)s][%(levelname)s][%(module)s]"
55+
"[%(funcName)s][%(lineno)d]: %(message)s"
56+
)
57+
PASSWORD_REGEX: str = (
58+
"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?" "[#?!@$%^&*-]).{8,14}$"
59+
)
60+
SUMMARY: str = """This backend project is a small demo as a use case for
61+
FastAPI with Pydantic and SQLAlchemy..
62+
"""
63+
DESCRIPTION: str = f"""**FastAPI**, **Pydantic** and **SQLAlchemy** helps
64+
you do awesome stuff. 🚀
65+
\n\n<img src="data:image/png;base64,{img_b64}"/>"""
66+
LICENSE_INFO: dict[str, str] = {
67+
"name": "MIT",
68+
"identifier": "MIT",
69+
}
70+
TAGS_METADATA: list[dict[str, str]] = [
71+
{
72+
"name": "user",
73+
"description": f"""Operations with users, such as register, get,
74+
update and delete.\n\n<img src="data:image/png;base64,
75+
{users_b64}" width="150" height="100"/>""",
76+
},
77+
]
78+
USER_CREATE_EXAMPLES: dict[str, Example] = {
79+
"normal": {
80+
"summary": "A normal example",
81+
"description": "A **normal** user create object that works "
82+
"correctly.",
83+
"value": {
84+
"username": "username",
85+
"email": "[email protected]",
86+
"password": "Hk7pH9*35Fu&3U",
87+
"birthdate": date(2004, 12, 31),
88+
"phone_number": PhoneNumber("+593987654321"),
89+
},
90+
},
91+
"converted": {
92+
"summary": "An example with converted data",
93+
"description": "FastAPI can convert phone number `strings` to "
94+
"actual `numbers` automatically",
95+
"value": {
96+
"username": "username",
97+
"email": "[email protected]",
98+
"password": "Hk7pH9*35Fu&3U",
99+
"birthdate": date(2004, 12, 31),
100+
"phone_number": PhoneNumber(593987654321),
101+
},
102+
},
103+
"invalid": {
104+
"summary": "Invalid data is rejected with an error",
105+
"value": {
106+
"username": "username",
107+
"email": "username",
108+
"password": "miclave123",
109+
"birthdate": date(95, 12, 31),
110+
"phone_number": PhoneNumber("5939876a4321"),
111+
},
112+
},
113+
}
114+
USER_UPDATE_EXAMPLES: dict[str, Example] = {
115+
"normal": {
116+
"summary": "A normal example",
117+
"description": "A **normal** user update object that works "
118+
"correctly.",
119+
"value": {
120+
"username": "username",
121+
"email": "[email protected]",
122+
"password": "Hk7pH9*35Fu&3U",
123+
"birthdate": date(2004, 12, 31),
124+
"phone_number": PhoneNumber(593987654321),
125+
},
126+
},
127+
"converted": {
128+
"summary": "An example with converted data",
129+
"description": "FastAPI can convert phone numbers `strings` to "
130+
"actual `numbers` automatically",
131+
"value": {
132+
"username": "username",
133+
"email": "[email protected]",
134+
"password": "Hk7pH9*35Fu&3U",
135+
"birthdate": date(2004, 12, 31),
136+
"phone_number": PhoneNumber("593987654321"),
137+
},
138+
},
139+
"invalid": {
140+
"summary": "Invalid data is rejected with an error",
141+
"value": {
142+
"username": "username",
143+
"email": "username",
144+
"password": "miclave123",
145+
"birthdate": date(95, 12, 31),
146+
"phone_number": PhoneNumber("59398x54321"),
147+
},
148+
},
149+
}
150+
LIMIT_EXAMPLES: dict[str, Example] = {
151+
"normal": {
152+
"summary": "A normal example",
153+
"description": "A **normal** limit query parameter that works "
154+
"correctly.",
155+
"value": 1,
156+
},
157+
"converted": {
158+
"summary": "An example with converted data",
159+
"description": "FastAPI can convert limit `strings` to actual"
160+
" `numbers` automatically",
161+
"value": "5",
162+
},
163+
"invalid": {
164+
"summary": "Invalid data is rejected with an error",
165+
"value": -1,
166+
},
167+
}
168+
SKIP_EXAMPLES: dict[str, Example] = {
169+
"normal": {
170+
"summary": "A normal example",
171+
"description": "A **normal** skip query parameter that works "
172+
"correctly.",
173+
"value": 0,
174+
},
175+
"converted": {
176+
"summary": "An example with converted data",
177+
"description": "FastAPI can convert skip `strings` to actual"
178+
" `numbers` automatically",
179+
"value": "20",
180+
},
181+
"invalid": {
182+
"summary": "Invalid data is rejected with an error",
183+
"value": -1,
184+
},
185+
}
186+
187+
188+
@lru_cache
189+
def get_init_settings() -> InitSettings:
190+
"""
191+
Get settings cached
192+
:return: The settings instance
193+
:rtype: Settings
194+
"""
195+
return InitSettings()
196+
197+
198+
init_setting: InitSettings = get_init_settings()

0 commit comments

Comments
 (0)