From a1923c6b6b5c880e9acc59ab3d99e820901c5b74 Mon Sep 17 00:00:00 2001 From: Ryan Partridge Date: Mon, 24 Jun 2024 21:52:26 +0100 Subject: [PATCH] Update backend runtime, backend-frontend connection, and console output - Change 'API_URL' to a specific localhost port for simplicity and rename it for clarity - Add 'update me' not to backend 'settings.py' for clarity - Update docstring for 'ExampleDB' in backend for clarity - Refactor backend 'build.py' -> 'start.py' and move into project for 'app' directory for Poetry script configuration - Add details in console, after project build, to inform next steps - Update backend run command to 'app-start' - Update README --- README.md | 86 ++++++++++++++++--- _project_demo/.env.local.example | 4 +- _project_demo/backend/__init__.py | 0 _project_demo/backend/app/config/settings.py | 6 +- _project_demo/backend/app/models/__init__.py | 6 +- .../backend/{build.py => app/start.py} | 4 +- _project_demo/backend/poetry.lock | 12 +-- _project_demo/backend/pyproject.toml | 4 +- _project_demo/frontend/next.config.mjs | 2 +- create_api_app/conf/constants/content.py | 8 +- create_api_app/conf/constants/filepaths.py | 4 + create_api_app/main.py | 15 +++- .../setup_assets/backend/__init__.py | 0 .../backend/app/config/settings.py | 6 +- .../backend/app/models/__init__.py | 6 +- .../backend/{build.py => app/start.py} | 4 +- .../setup_assets/frontend/next.config.mjs | 2 +- create_api_app/setup_assets/root/.env.local | 4 +- pyproject.toml | 3 +- tests/mappings/__init__.py | 2 +- 20 files changed, 130 insertions(+), 48 deletions(-) delete mode 100644 _project_demo/backend/__init__.py rename _project_demo/backend/{build.py => app/start.py} (84%) delete mode 100644 create_api_app/setup_assets/backend/__init__.py rename create_api_app/setup_assets/backend/{build.py => app/start.py} (84%) diff --git a/README.md b/README.md index d53e162..a0fff1e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ -# Create API App Quickstart Tool +# Create API App Quickstart Tool -Welcome to the quickstart tool for creating a `FastAPI` project with a `NextJS` frontend. +![PyPI - Python Version](https://img.shields.io/pypi/pyversions/create-api-app?style=flat&color=green) -This tool is intended to be dynamic and installs the most recent packages where possible, while maintaining compatibility across the main OS's (Mac, Linux and Windows). You can use the tool by installing the `PIP` package. See the [Using The Tool section](#using-the-tool) for more details. +Welcome to the quickstart tool for creating a `FastAPI` project with a `NextJS` frontend. -If there are any issues using the tool, please flag them in the [issues](https://github.com/Achronus/create-api-app/issues) section of this repository. +This tool creates a predefined template while installing the most recent packages where possible. Found on: - [PyPi](https://pypi.org/project/create-api-app/) - [GitHub](https://github.com/Achronus/create-api-app/) +## Contents + +- [Why This Tool?](#why-this-tool) +- [The Stack](#the-stack) +- [Installation](#installation) +- [Running The Backend](#running-the-backend) +- [Running the Frontend](#running-the-frontend) +- [Customization](#customization) + ## Why This Tool? Creating a project from scratch can be a tedious process. Not only do you have to create all the files yourself, it typically requires a lot of small minor changes that can easily be automated. So, rather than wasting a lot of time setting up projects, I created a tool that does it all for me! @@ -24,6 +33,7 @@ All projects are created using the same stack, consisting of the following: 1. Backend - [FastAPI](https://github.com/tiangolo/fastapi) + - [Pydantic](https://docs.pydantic.dev/) - [MongoDB](https://www.mongodb.com/) - [Beanie](https://beanie-odm.dev/) - [Poetry](https://python-poetry.org/) @@ -42,26 +52,78 @@ All projects are created using the same stack, consisting of the following: _Note: all libraries and packages are automatically installed to their latest versions when running the tool._ -### Useful Styling Options - -- [Clerk Themes](https://clerk.com/docs/components/customization/themes) -- [Shadcn UI Theme Generator](https://gradient.page/tools/shadcn-ui-theme-generator) -- [Modern Background Snippets](https://bg.ibelick.com/) +We've also added some extra files too! You can find out more about them in our [documentation](https://create.achronus.dev/file-structure/). -## Using The Tool +## Installation 1. Firstly, install [Docker](https://docs.docker.com/get-docker/), we use this to create the frontend files dynamically using the [Build NextJS App Tool](https://github.com/Achronus/build-nextjs-app). -2. Install the package through `PIP` using the following command (requires `Python 3.12` minimum): +2. Install the package through `PIP`: ```python pip install create_api_app ``` -3. Create a project with the following command: +3. Create a project: ```python create-api-app ``` And that's it! You'll find two folders in your project, one called `frontend` (for NextJS) and another called `backend` (for FastAPI). + +## Running The Backend + +1. Open a terminal and navigate to the `backend` directory: + + ```cmd + cd backend + ``` + +2. Install a virtual environment for `Poetry`: + + ```cmd + python -m venv env + ``` + +3. Access it using one of the following (first -> `Windows`; second -> `Mac/Linux`): + + ```cmd + .\env\Scripts\activate + ``` + + ```cmd + source ./env/bin/activate + ``` + + _Not working? Refer to the [virtual env docs](https://docs.python.org/3/library/venv.html#how-venvs-work)._ + +4. Run the `uvicorn` server using the `Poetry` script: + + ```cmd + app-start + ``` + +## Running the Frontend + +1. Open a terminal and navigate to the `frontend` directory: + + ```cmd + cd frontend + ``` + +2. Install the packages using [Node.js](https://nodejs.org/en): + + ```cmd + npm install + ``` + +3. Run the development server: + + ```cmd + npm run dev + ``` + +## Customization + +Customization options are found in our [documentation](https://create.achronus.dev/customization/). diff --git a/_project_demo/.env.local.example b/_project_demo/.env.local.example index 5e10520..437cca6 100644 --- a/_project_demo/.env.local.example +++ b/_project_demo/.env.local.example @@ -1,5 +1,5 @@ -# An optional API URL only used when populating your database with data from an existing API. Refer to 'backend/db_insert.py' -API_URL= +# The URL to connect FastAPI and NextJS together - used in `frontend/next.config.mjs` +FASTAPI_CONNECTION_URL=http://127.0.0.1:8000/ # MongoDB: The connection url to your database # https://www.mongodb.com/ diff --git a/_project_demo/backend/__init__.py b/_project_demo/backend/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/_project_demo/backend/app/config/settings.py b/_project_demo/backend/app/config/settings.py index 8a218a7..c93a3d7 100644 --- a/_project_demo/backend/app/config/settings.py +++ b/_project_demo/backend/app/config/settings.py @@ -1,6 +1,6 @@ import os -from utils.fileloader import FileLoader +from app.utils.fileloader import FileLoader class Settings: @@ -10,8 +10,8 @@ class Settings: FILEPATHS = __fileloader.FILEPATHS DB_URL = os.getenv("DATABASE_URL") - DB_NAME = "" - DB_COLLECTION_NAME = "" + DB_NAME = "" # Update me! + DB_COLLECTION_NAME = "" # Update me! settings = Settings() diff --git a/_project_demo/backend/app/models/__init__.py b/_project_demo/backend/app/models/__init__.py index d6fa4b0..c349903 100644 --- a/_project_demo/backend/app/models/__init__.py +++ b/_project_demo/backend/app/models/__init__.py @@ -7,7 +7,11 @@ class ExampleDB(Document): - """The main model for database collection based on the database.""" + """ + The main model for your database collection. Should represent the structure of the data in the collection. + + For more details check the [Beanie docs](https://beanie-odm.dev/). + """ name: str desc: Optional[str] = None diff --git a/_project_demo/backend/build.py b/_project_demo/backend/app/start.py similarity index 84% rename from _project_demo/backend/build.py rename to _project_demo/backend/app/start.py index b60badd..492918e 100644 --- a/_project_demo/backend/build.py +++ b/_project_demo/backend/app/start.py @@ -3,7 +3,7 @@ import uvicorn -def start(env_mode: str = "dev", host: str = "127.0.0.1", port: int = 8000) -> None: +def run(env_mode: str = "dev", host: str = "127.0.0.1", port: int = 8000) -> None: """Start the server.""" dev_mode = True if env_mode == "dev" else False @@ -25,4 +25,4 @@ def start(env_mode: str = "dev", host: str = "127.0.0.1", port: int = 8000) -> N parser.add_argument("-pt", "--port", type=int, default=8000, required=False) args = parser.parse_args() - start(args.env, args.host, args.port) + run(args.env, args.host, args.port) diff --git a/_project_demo/backend/poetry.lock b/_project_demo/backend/poetry.lock index d245438..2052476 100644 --- a/_project_demo/backend/poetry.lock +++ b/_project_demo/backend/poetry.lock @@ -652,13 +652,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "hypothesis" -version = "6.103.2" +version = "6.103.5" description = "A library for property-based testing" optional = false python-versions = ">=3.8" files = [ - {file = "hypothesis-6.103.2-py3-none-any.whl", hash = "sha256:629b7cdeca8c225933739f99879caba21949000d2c919c8b4585e01048b3bc73"}, - {file = "hypothesis-6.103.2.tar.gz", hash = "sha256:83504e31e90a0d7d6e8eb93e51525dc1a48d79c932a50ad6035e29f8295328cd"}, + {file = "hypothesis-6.103.5-py3-none-any.whl", hash = "sha256:8eea4ef1542081592f43ae0aeefdf088780cdd40cfc108e7adf5ff3ef00952e2"}, + {file = "hypothesis-6.103.5.tar.gz", hash = "sha256:9c455baa7bf0a40538e9afeb2ab21d91bc1d2a5a8db1a3536df868cfc605f307"}, ] [package.dependencies] @@ -666,10 +666,10 @@ attrs = ">=22.2.0" sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.54)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.4)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.1)"] +all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.55)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.4)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.1)"] cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] -crosshair = ["crosshair-tool (>=0.0.54)", "hypothesis-crosshair (>=0.0.4)"] +crosshair = ["crosshair-tool (>=0.0.55)", "hypothesis-crosshair (>=0.0.4)"] dateutil = ["python-dateutil (>=1.4)"] django = ["django (>=3.2)"] dpcontracts = ["dpcontracts (>=0.4)"] @@ -1949,4 +1949,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "1d41954a49507c54c35b5073adfe0a531658d6a80d9ca1bdc5d3acf503eaf4cc" +content-hash = "b6187e27f9b2a56643346c9bd6928f3d66ae5e62ba4c0d481eff123e2c439510" diff --git a/_project_demo/backend/pyproject.toml b/_project_demo/backend/pyproject.toml index 2409022..56f6165 100644 --- a/_project_demo/backend/pyproject.toml +++ b/_project_demo/backend/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Ryan Partridge "] readme = "README.md" [tool.poetry.scripts] -run = "build:start" +app-start = "app.start:run" [tool.poetry.dependencies] python = "^3.12" @@ -19,7 +19,7 @@ beanie = "^1.26.0" [tool.poetry.group.dev.dependencies] pytest = "^8.2.2" pytest-cov = "^5.0.0" -hypothesis = "^6.103.2" +hypothesis = "^6.103.5" aiohttp = "^3.9.5" requests = "^2.32.3" diff --git a/_project_demo/frontend/next.config.mjs b/_project_demo/frontend/next.config.mjs index 12b5542..1e684e5 100644 --- a/_project_demo/frontend/next.config.mjs +++ b/_project_demo/frontend/next.config.mjs @@ -11,7 +11,7 @@ const loadEnv = (filePath) => { loadEnv(path.resolve(process.cwd(), ".env.local")); -const apiUrl = process.env.API_URL; +const apiUrl = process.env.FASTAPI_CONNECTION_URL; const nextConfig = { images: { diff --git a/create_api_app/conf/constants/content.py b/create_api_app/conf/constants/content.py index 41488ea..d3e85e6 100644 --- a/create_api_app/conf/constants/content.py +++ b/create_api_app/conf/constants/content.py @@ -1,11 +1,9 @@ -from .filepaths import AssetFilenames - - class PoetryContent: """A helper class for retrieving content for the Poetry installation.""" def __init__(self) -> None: - self.START_SERVER_CMD = f"{AssetFilenames.BUILD.split('.')[0]}:start" + self.start_server_cmd = "app-start" + self.start_server_location = "app.start:run" def pyproject_desc(self) -> str: return 'description = "A FastAPI backend for processing API data and passing it to the frontend."' @@ -14,7 +12,7 @@ def pyproject_author(self) -> str: return "rpartridge101@gmail.com" def pyproject_scripts(self) -> str: - return f'\n\n[tool.poetry.scripts]\nrun = "{self.START_SERVER_CMD}"\n\n' + return f'\n\n[tool.poetry.scripts]\n{self.start_server_cmd} = "{self.start_server_location}"\n\n' class FrontendContent: diff --git a/create_api_app/conf/constants/filepaths.py b/create_api_app/conf/constants/filepaths.py index 35003c7..57050f0 100644 --- a/create_api_app/conf/constants/filepaths.py +++ b/create_api_app/conf/constants/filepaths.py @@ -68,3 +68,7 @@ def __init__(self, project_name: str = None) -> None: self.POETRY_CONF = os.path.join(self.BACKEND, AssetFilenames.POETRY_CONF) self.TAILWIND_CONF = os.path.join(self.FRONTEND, AssetFilenames.TAILWIND) + + self.ENV_LOCAL = os.path.join(self.ROOT, ".env.local") + self.SETTINGS = os.path.join(self.BACKEND_APP, "config", "settings.py") + self.MODELS = os.path.join(self.BACKEND_APP, "models", "__init__.py") diff --git a/create_api_app/main.py b/create_api_app/main.py index 80c2636..19a40c2 100644 --- a/create_api_app/main.py +++ b/create_api_app/main.py @@ -1,5 +1,6 @@ import os import shutil +import textwrap import time from create_api_app.setup.clean import CleanupController @@ -9,7 +10,7 @@ ) from create_api_app.setup.backend import VEnvController, BackendStaticAssetController -from .conf.constants.filepaths import set_project_name +from .conf.constants.filepaths import ProjectPaths, set_project_name from .setup import run_frontend_tasks, run_tasks from .utils.helper import strip_whitespace_and_dashes from .utils.printables import project_table, project_complete_panel @@ -134,7 +135,17 @@ def main( # End of script console.print(project_complete_panel()) console.print( - f"Access the project files here [link={os.getcwd()}]{name_print}[/link]\n" + f"Access the project files here [link={os.getcwd()}]{name_print}[/link]" + ) + + project_paths = ProjectPaths(name) + console.print( + textwrap.dedent(f""" + [dark_goldenrod]Not sure where to start?[/dark_goldenrod] + - [green][link={project_paths.ENV_LOCAL}].env.local[/link][/green] - Update your API keys + - [yellow][link={project_paths.SETTINGS}]config/settings.py[/link][/yellow] - Update the [yellow]DB_NAME[/yellow] and [yellow]DB_COLLECTION_NAME[/yellow] for your [green]MongoDB[/green] database + - [yellow][link={project_paths.MODELS}]models/__init__.py[/link][/yellow] - Update the [green]ExampleDB[/green] model\n + """) ) diff --git a/create_api_app/setup_assets/backend/__init__.py b/create_api_app/setup_assets/backend/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/create_api_app/setup_assets/backend/app/config/settings.py b/create_api_app/setup_assets/backend/app/config/settings.py index 8a218a7..c93a3d7 100644 --- a/create_api_app/setup_assets/backend/app/config/settings.py +++ b/create_api_app/setup_assets/backend/app/config/settings.py @@ -1,6 +1,6 @@ import os -from utils.fileloader import FileLoader +from app.utils.fileloader import FileLoader class Settings: @@ -10,8 +10,8 @@ class Settings: FILEPATHS = __fileloader.FILEPATHS DB_URL = os.getenv("DATABASE_URL") - DB_NAME = "" - DB_COLLECTION_NAME = "" + DB_NAME = "" # Update me! + DB_COLLECTION_NAME = "" # Update me! settings = Settings() diff --git a/create_api_app/setup_assets/backend/app/models/__init__.py b/create_api_app/setup_assets/backend/app/models/__init__.py index d6fa4b0..c349903 100644 --- a/create_api_app/setup_assets/backend/app/models/__init__.py +++ b/create_api_app/setup_assets/backend/app/models/__init__.py @@ -7,7 +7,11 @@ class ExampleDB(Document): - """The main model for database collection based on the database.""" + """ + The main model for your database collection. Should represent the structure of the data in the collection. + + For more details check the [Beanie docs](https://beanie-odm.dev/). + """ name: str desc: Optional[str] = None diff --git a/create_api_app/setup_assets/backend/build.py b/create_api_app/setup_assets/backend/app/start.py similarity index 84% rename from create_api_app/setup_assets/backend/build.py rename to create_api_app/setup_assets/backend/app/start.py index b60badd..492918e 100644 --- a/create_api_app/setup_assets/backend/build.py +++ b/create_api_app/setup_assets/backend/app/start.py @@ -3,7 +3,7 @@ import uvicorn -def start(env_mode: str = "dev", host: str = "127.0.0.1", port: int = 8000) -> None: +def run(env_mode: str = "dev", host: str = "127.0.0.1", port: int = 8000) -> None: """Start the server.""" dev_mode = True if env_mode == "dev" else False @@ -25,4 +25,4 @@ def start(env_mode: str = "dev", host: str = "127.0.0.1", port: int = 8000) -> N parser.add_argument("-pt", "--port", type=int, default=8000, required=False) args = parser.parse_args() - start(args.env, args.host, args.port) + run(args.env, args.host, args.port) diff --git a/create_api_app/setup_assets/frontend/next.config.mjs b/create_api_app/setup_assets/frontend/next.config.mjs index 12b5542..1e684e5 100644 --- a/create_api_app/setup_assets/frontend/next.config.mjs +++ b/create_api_app/setup_assets/frontend/next.config.mjs @@ -11,7 +11,7 @@ const loadEnv = (filePath) => { loadEnv(path.resolve(process.cwd(), ".env.local")); -const apiUrl = process.env.API_URL; +const apiUrl = process.env.FASTAPI_CONNECTION_URL; const nextConfig = { images: { diff --git a/create_api_app/setup_assets/root/.env.local b/create_api_app/setup_assets/root/.env.local index 5e10520..437cca6 100644 --- a/create_api_app/setup_assets/root/.env.local +++ b/create_api_app/setup_assets/root/.env.local @@ -1,5 +1,5 @@ -# An optional API URL only used when populating your database with data from an existing API. Refer to 'backend/db_insert.py' -API_URL= +# The URL to connect FastAPI and NextJS together - used in `frontend/next.config.mjs` +FASTAPI_CONNECTION_URL=http://127.0.0.1:8000/ # MongoDB: The connection url to your database # https://www.mongodb.com/ diff --git a/pyproject.toml b/pyproject.toml index 2781529..e679076 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "create-api-app" -version = "2.0.1" +version = "2.0.2" description = "A quickstart tool for creating a FastAPI project with a NextJS frontend." authors = ["Ryan Partridge "] readme = "README.md" @@ -15,7 +15,6 @@ pytest = "^8.2.2" build_nextjs_app = "^1.0.2" docker = "^7.1.0" - [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/mappings/__init__.py b/tests/mappings/__init__.py index e0b1da0..acc263f 100644 --- a/tests/mappings/__init__.py +++ b/tests/mappings/__init__.py @@ -7,7 +7,7 @@ 'readme = "README.md"\n', "\n", "[tool.poetry.scripts]\n", - 'run = "build:start"\n', + 'app-start = "app.start:run"\n', "\n", ]