diff --git a/{{ cookiecutter.project_slug }}/.gitignore b/{{ cookiecutter.project_slug }}/.gitignore
index bdaff43..dab990d 100644
--- a/{{ cookiecutter.project_slug }}/.gitignore
+++ b/{{ cookiecutter.project_slug }}/.gitignore
@@ -175,4 +175,5 @@ dmypy.json
.pyre/
.pytype/
cython_debug/
-cookiecutter.zip
\ No newline at end of file
+cookiecutter.zip
+envs.yml
diff --git a/{{ cookiecutter.project_slug }}/.idea/.gitignore b/{{ cookiecutter.project_slug }}/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/{{ cookiecutter.project_slug }}/.idea/inspectionProfiles/Project_Default.xml b/{{ cookiecutter.project_slug }}/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..03d9549
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/{{ cookiecutter.project_slug }}/.idea/inspectionProfiles/profiles_settings.xml b/{{ cookiecutter.project_slug }}/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/{{ cookiecutter.project_slug }}/.idea/misc.xml b/{{ cookiecutter.project_slug }}/.idea/misc.xml
new file mode 100644
index 0000000..f783827
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/{{ cookiecutter.project_slug }}/.idea/modules.xml b/{{ cookiecutter.project_slug }}/.idea/modules.xml
new file mode 100644
index 0000000..1418fcd
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Create_migration.xml b/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Create_migration.xml
new file mode 100644
index 0000000..b9e529d
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Create_migration.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Run.xml b/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Run.xml
new file mode 100644
index 0000000..6b83792
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Run.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Run_Migrations.xml b/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Run_Migrations.xml
new file mode 100644
index 0000000..0461cea
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/runConfigurations/Run_Migrations.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/{{ cookiecutter.project_slug }}/.idea/vcs.xml b/{{ cookiecutter.project_slug }}/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/{{ cookiecutter.project_slug }}/.idea/{{ cookiecutter.project_slug }}.iml b/{{ cookiecutter.project_slug }}/.idea/{{ cookiecutter.project_slug }}.iml
new file mode 100644
index 0000000..00b113c
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.idea/{{ cookiecutter.project_slug }}.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/{{ cookiecutter.project_slug }}/Dockerfile b/{{ cookiecutter.project_slug }}/Dockerfile
index a3280df..685bc65 100644
--- a/{{ cookiecutter.project_slug }}/Dockerfile
+++ b/{{ cookiecutter.project_slug }}/Dockerfile
@@ -1,13 +1,13 @@
-FROM python:3.12-alpine as python
+FROM python:3.12-alpine AS python
WORKDIR /app
-ENV PYTHONUNBUFFERED 1
+ARG ENVIRONMENT=local
+ENV PYTHONUNBUFFERED=1
ENV TZ="Europe/Riga"
COPY requirements /app/requirements
RUN apk add libpq \
poppler-utils zlib-dev \
-# curl \
- && pip install --no-cache-dir --upgrade -r requirements/production.txt \
+ && pip install --no-cache-dir --upgrade -r requirements/${ENVIRONMENT}.txt \
&& rm -rf /var/cache/apk/*
COPY entrypoint.sh /entrypoint
@@ -19,4 +19,4 @@ RUN chmod +x /entrypoint \
EXPOSE 5000
ENTRYPOINT ["/entrypoint"]
-CMD ["uvicorn", "--workers", "2", "--proxy-headers", "--host", "0.0.0.0", "--port", "5000", "--forwarded-allow-ips=*", "service.main:app"]
+CMD ["uvicorn", "--workers", "2", "--proxy-headers", "--host", "0.0.0.0", "--port", "5000", "--forwarded-allow-ips=*", "service.api.main:app"]
diff --git a/{{ cookiecutter.project_slug }}/ENVIRON.md b/{{ cookiecutter.project_slug }}/ENVIRON.md
index e8a497d..21f6cd5 100644
--- a/{{ cookiecutter.project_slug }}/ENVIRON.md
+++ b/{{ cookiecutter.project_slug }}/ENVIRON.md
@@ -48,7 +48,7 @@
# `SERVICE_DATABASE_URL`
-*Optional*, default value: `psql://{{ cookiecutter.project_slug }}:{{ cookiecutter.project_slug }}@postgres:5432/{{ cookiecutter.project_slug }}`
+*Optional*, default value: `asyncpg://{{ cookiecutter.project_slug }}:{{ cookiecutter.project_slug }}@postgres:5432/{{ cookiecutter.project_slug }}`
# `SERVICE_TIMEZONE`
diff --git a/{{ cookiecutter.project_slug }}/README.md b/{{ cookiecutter.project_slug }}/README.md
new file mode 100644
index 0000000..ee40648
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/README.md
@@ -0,0 +1,37 @@
+# Welcome to {{ cookiecutter.project_name }}
+
+## Setup
+### 1. Install dependencies
+#### Local virtual environment
+```shell
+python3 -m venv venv
+source venv/bin/activate
+pip install -r requirements/local.txt
+```
+
+#### Docker image
+```shell
+docker build -f Dockerfile --tag {{ cookiecutter.project_slug }} .
+```
+
+### 2. Initial setup
+#### Environment variables
+Environment variables for the project are being read from the env.yml file:
+- Copy example yml:
+`cp envs.example.yml envs.yml`
+- Adjust values as necessary
+
+#### Initialize database
+```shell
+aerich init-db
+```
+
+#### Migrate database
+```shell
+aerich upgrade
+```
+
+#### Create new database migration
+```shell
+aerich migrate
+```
diff --git a/{{ cookiecutter.project_slug }}/envs.example.yml b/{{ cookiecutter.project_slug }}/envs.example.yml
index 8f3e8b0..fee131f 100644
--- a/{{ cookiecutter.project_slug }}/envs.example.yml
+++ b/{{ cookiecutter.project_slug }}/envs.example.yml
@@ -7,13 +7,13 @@ service:
token_expiration_days: 28
cors_origins:
- "http://localhost:5000"
- - "https://example.com"
+ - "https://{{ cookiecutter.project_slug }}.xyz"
environment: "local"
log_level: "DEBUG"
redis_url: "redis://redis:6379"
- database_url: "psql://{{ cookiecutter.project_slug }}:{{ cookiecutter.project_slug }}@postgres:5432/{{ cookiecutter.project_slug }}"
+ database_url: "asyncpg://{{ cookiecutter.project_slug }}:{{ cookiecutter.project_slug }}@postgres:5432/{{ cookiecutter.project_slug }}"
timezone: "UTC"
sentry_url: null
diff --git a/{{ cookiecutter.project_slug }}/requirements/base.txt b/{{ cookiecutter.project_slug }}/requirements/base.txt
index 460cb5d..b411687 100644
--- a/{{ cookiecutter.project_slug }}/requirements/base.txt
+++ b/{{ cookiecutter.project_slug }}/requirements/base.txt
@@ -1,26 +1,26 @@
-fastapi==0.115.5 # https://github.com/tiangolo/fastapi
-uvicorn==0.32.1 # https://pypi.org/project/uvicorn/
+fastapi==0.115.12 # https://github.com/tiangolo/fastapi
+uvicorn==0.34.0 # https://pypi.org/project/uvicorn/
-pydantic[email]==2.10.2 # https://github.com/pydantic/pydantic
-pydantic-settings[yaml]==2.6.1 # https://github.com/pydantic/pydantic-settings/
+pydantic[email]==2.11.2 # https://github.com/pydantic/pydantic
+pydantic-settings[yaml]==2.8.1 # https://github.com/pydantic/pydantic-settings/
-tortoise-orm[accel,asyncpg]==0.22.1 # https://pypi.org/project/tortoise-orm/
-aerich==0.7.2 # https://pypi.org/project/aerich/
+tortoise-orm[accel,asyncpg]==0.24.2 # https://pypi.org/project/tortoise-orm/
+aerich==0.8.2 # https://pypi.org/project/aerich/
setech==1.4.2 # https://pypi.org/project/setech/
-python-multipart==0.0.18 # https://pypi.org/project/python-multipart/
+python-multipart==0.0.20 # https://pypi.org/project/python-multipart/
email-validator==2.2.0 # https://pypi.org/project/email-validator/
-tenacity==9.0.0 # https://pypi.org/project/tenacity/
-pydantic==2.10.2 # https://pypi.org/project/pydantic/
+tenacity==9.1.2 # https://pypi.org/project/tenacity/
+pydantic==2.11.2 # https://pypi.org/project/pydantic/
#emails==0.6 # https://pypi.org/project/emails/
-python-jose[cryptography]==3.3 # https://pypi.org/project/python-jose/
+python-jose[cryptography]==3.4.0 # https://pypi.org/project/python-jose/
passlib[bcrypt]==1.7.4 # https://pypi.org/project/passlib/
-bcrypt==4.2.1 # https://pypi.org/project/bcrypt/
+bcrypt==4.3.0 # https://pypi.org/project/bcrypt/
# Pin bcrypt until passlib supports the latest
-pydantic-settings==2.6.1 # https://pypi.org/project/pydantic-settings/
+pydantic-settings==2.8.1 # https://pypi.org/project/pydantic-settings/
asyncio==3.4.3
diff --git a/{{ cookiecutter.project_slug }}/requirements/local.txt b/{{ cookiecutter.project_slug }}/requirements/local.txt
index cf7beb7..ff04d1e 100644
--- a/{{ cookiecutter.project_slug }}/requirements/local.txt
+++ b/{{ cookiecutter.project_slug }}/requirements/local.txt
@@ -1,25 +1,25 @@
-r base.txt
-uvicorn[standard]==0.32.1 # https://pypi.org/project/uvicorn/
+uvicorn[standard]==0.34.0 # https://pypi.org/project/uvicorn/
-black==24.10.0 # https://pypi.org/project/black/
-isort==5.13.2 # https://pypi.org/project/isort/
-pur==7.3.2 # https://pypi.org/project/pur/
-pre-commit==4.0.1 # https://pypi.org/project/pre-commit/
-flake8==7.1.1
+black==25.1.0 # https://pypi.org/project/black/
+isort==6.0.1 # https://pypi.org/project/isort/
+pur==7.3.3 # https://pypi.org/project/pur/
+pre-commit==4.2.0 # https://pypi.org/project/pre-commit/
+flake8==7.2.0
-pytest==8.3.3 # https://pypi.org/project/pytest/
-coverage==7.6.8 # https://pypi.org/project/coverage/
+pytest==8.3.5 # https://pypi.org/project/pytest/
+coverage==7.8.0 # https://pypi.org/project/coverage/
-mypy==1.13.0 # https://pypi.org/project/mypy/
-types-python-jose==3.3.4.20240106 # https://pypi.org/project/types-python-jose/
-types-passlib==1.7.7.20240819 # https://pypi.org/project/types-passlib/
-types-PyYAML==6.0.12.20240917
-types-Pygments==2.18.0.20240506
+mypy==1.15.0 # https://pypi.org/project/mypy/
+types-python-jose==3.4.0.20250224 # https://pypi.org/project/types-python-jose/
+types-passlib==1.7.7.20250401 # https://pypi.org/project/types-passlib/
+types-PyYAML==6.0.12.20250402
+types-Pygments==2.19.0.20250305
types-colorama==0.4.15.20240311
-types-decorator==5.1.8.20240310
-types-six==1.16.21.20241105
-types-ujson==5.10.0.20240515
+types-decorator==5.2.0.20250324
+types-six==1.17.0.20250403
+types-ujson==5.10.0.20250326
-settings-doc==4.3.1 # https://github.com/radeklat/settings-doc
-ipython==8.30.0
+settings-doc==4.3.2 # https://github.com/radeklat/settings-doc
+ipython==9.0.2
diff --git a/{{ cookiecutter.project_slug }}/requirements/production.txt b/{{ cookiecutter.project_slug }}/requirements/production.txt
index dc26733..00daef6 100644
--- a/{{ cookiecutter.project_slug }}/requirements/production.txt
+++ b/{{ cookiecutter.project_slug }}/requirements/production.txt
@@ -1,3 +1,3 @@
-r base.txt
-sentry-sdk[fastapi]==2.19.0 # https://pypi.org/project/sentry-sdk/
+sentry-sdk[fastapi]==2.25.1 # https://pypi.org/project/sentry-sdk/
diff --git a/{{ cookiecutter.project_slug }}/service/config/_settings.py b/{{ cookiecutter.project_slug }}/service/config/_settings.py
index 7572e8f..c3ed543 100644
--- a/{{ cookiecutter.project_slug }}/service/config/_settings.py
+++ b/{{ cookiecutter.project_slug }}/service/config/_settings.py
@@ -24,7 +24,7 @@ class ProjectSettings(ProjectBaseSettings):
# Background task config
redis_url: RedisDsn = Field(RedisDsn(url="redis://redis:6379"))
- database_url: str = Field("psql://{{ cookiecutter.project_slug }}:{{ cookiecutter.project_slug }}@postgres:5432/{{ cookiecutter.project_slug }}")
+ database_url: str = Field("asyncpg://{{ cookiecutter.project_slug }}:{{ cookiecutter.project_slug }}@postgres:5432/{{ cookiecutter.project_slug }}")
# Various
timezone: str = Field(
diff --git a/{{ cookiecutter.project_slug }}/service/crud/user/methods.py b/{{ cookiecutter.project_slug }}/service/crud/user/methods.py
index 54c6a74..33681ce 100644
--- a/{{ cookiecutter.project_slug }}/service/crud/user/methods.py
+++ b/{{ cookiecutter.project_slug }}/service/crud/user/methods.py
@@ -1,9 +1,8 @@
+from service.api.dependencies import QueryParams
from service.core.security import verify_password
+from service.crud.user.models import UserPublic, UserRegister
+from service.crud.utils import order_queryset
from service.database.models import User
-from ..utils import order_queryset
-
-from ...api.dependencies import QueryParams
-from .models import UserPublic, UserRegister
async def authenticate(*, username: str, password: str) -> User | None:
diff --git a/{{ cookiecutter.project_slug }}/service/crud/utils.py b/{{ cookiecutter.project_slug }}/service/crud/utils.py
index a9c57e8..fb87819 100644
--- a/{{ cookiecutter.project_slug }}/service/crud/utils.py
+++ b/{{ cookiecutter.project_slug }}/service/crud/utils.py
@@ -4,5 +4,7 @@ from service.constants.types import PaginationParams
def order_queryset(qs: QuerySet, filters: PaginationParams, default: str) -> QuerySet:
- ordering = [f for f in filters.order.split(",") if f.split("-")[-1] in qs.fields]
+ ordering = None
+ if filters.order:
+ ordering = [f for f in filters.order.split(",") if f.split("-")[-1] in qs.fields]
return qs.order_by(*(ordering or (default, )))