├── .github ├── FUNDING.yml └── workflows │ └── auto_deploy.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── backend ├── .env.example ├── .gitignore ├── .pre-commit-config.yaml ├── .python-version ├── Makefile ├── README.md ├── api │ ├── __init__.py │ ├── __main__.py │ ├── app.py │ ├── config.py │ ├── entrypoints │ │ ├── __init__.py │ │ ├── monitoring │ │ │ ├── __init__.py │ │ │ └── views.py │ │ ├── router.py │ │ └── v1 │ │ │ ├── __init__.py │ │ │ ├── random_number │ │ │ ├── __init__.py │ │ │ ├── schema.py │ │ │ └── views.py │ │ │ ├── root_response │ │ │ ├── __init__.py │ │ │ ├── schema.py │ │ │ └── views.py │ │ │ └── router.py │ ├── services │ │ ├── random_number_service.py │ │ └── root_response_service.py │ ├── utils │ │ └── custom_decorators.py │ └── vercel.py ├── poetry.lock ├── pyproject.toml ├── requirements.txt ├── tests │ ├── __init__.py │ ├── conftest.py │ ├── e2e │ │ ├── __init__.py │ │ └── entrypoints │ │ │ ├── __init__.py │ │ │ ├── monitoring │ │ │ ├── __init__.py │ │ │ └── test_health.py │ │ │ └── v1 │ │ │ ├── __init__.py │ │ │ ├── random_number │ │ │ ├── __init__.py │ │ │ └── test_random_number_GET.py │ │ │ └── root_response │ │ │ ├── __init__.py │ │ │ └── test_root_response_GET.py │ └── unit │ │ ├── __init__.py │ │ ├── services │ │ ├── __init__.py │ │ ├── test_random_number_service.py │ │ └── test_root_response_service.py │ │ ├── test_vercel.py │ │ └── utils │ │ └── test_custom_decorators.py └── vercel.json ├── docs └── images │ ├── api-root-folder.png │ ├── github-secrets.png │ └── repo-scope-image.png └── frontend ├── .browserslistrc ├── .editorconfig ├── .env.example ├── .eslintrc-auto-import.json ├── .eslintrc.js ├── .gitignore ├── .nvmrc ├── Makefile ├── README.md ├── auto-imports.d.ts ├── bun.lockb ├── components.d.ts ├── index.html ├── package.json ├── public └── favicon.ico ├── src ├── App.vue ├── assets │ ├── logo.png │ └── logo.svg ├── components │ ├── HelloWorld.vue │ ├── README.md │ └── loaders │ │ ├── DualRing.vue │ │ ├── Ellipsis.vue │ │ ├── Ellipsis2.vue │ │ ├── Ring.vue │ │ └── Roller.vue ├── layouts │ ├── README.md │ ├── default.vue │ └── default │ │ ├── AppBar.vue │ │ └── View.vue ├── main.ts ├── pages │ ├── README.md │ ├── double-random.vue │ └── index.vue ├── plugins │ ├── README.md │ ├── index.ts │ └── vuetify.ts ├── router │ └── index.ts ├── store │ ├── README.md │ ├── app.ts │ └── index.ts ├── styles │ ├── README.md │ └── settings.scss └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── typed-router.d.ts └── vite.config.mts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ ArielMAJ ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/auto_deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | on: 3 | push: 4 | branches: ["main"] 5 | paths: 6 | - "frontend/**" 7 | - ".github/workflows/auto_deploy.yml" 8 | env: 9 | VITE_APP_BACKEND_ROOT_ENDPOINT: ${{ vars.VITE_APP_BACKEND_ROOT_ENDPOINT }} 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: oven-sh/setup-bun@v1 17 | with: 18 | bun-version: 1.0.26 19 | - name: setup git auth 20 | run: | 21 | git config --global credential.helper store 22 | echo "https://${{ secrets.ACCESS_TOKEN }}:x-oauth-basic@github.com" > ~/.git-credentials 23 | git config --global user.name $user_name 24 | git config --global user.email $user_email 25 | env: 26 | user_name: "ArielMAJ" 27 | user_email: "ariel.maj@hotmail.comm" 28 | - name: deploy to gh-pages 29 | run: | 30 | cd frontend 31 | bun install 32 | mkdir tmp 33 | cd tmp 34 | git init 35 | git remote add origin "https://github.com/${{ github.repository }}.git" 36 | git fetch origin gh-pages 37 | git checkout gh-pages 38 | find . -maxdepth 1 -type f ! -name "CNAME" -exec rm -v {} \; 39 | find . -maxdepth 1 -type d ! -name ".git" -exec rm -rv {} \; 40 | cd .. 41 | bun run build 42 | mv dist/* tmp 43 | cd tmp 44 | if [[ ! $(git status --porcelain) ]]; then 45 | echo "No changes to commit. Skipping deployment." 46 | exit 0 47 | fi 48 | git add -A 49 | git commit -m "🚀 Deploy id ${GITHUB_RUN_ID}" 50 | git push origin gh-pages 51 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | - Demonstrating empathy and kindness toward other people 21 | - Being respectful of differing opinions, viewpoints, and experiences 22 | - Giving and gracefully accepting constructive feedback 23 | - Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | - Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | - The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | - Trolling, insulting or derogatory comments, and personal or political attacks 33 | - Public or private harassment 34 | - Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | - Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | ariel.maj@hotmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), I encourage **everyone** to help improve this project. 4 | Here are some ways _you_ can contribute: 5 | 6 | - Report bugs. 7 | - Suggest new features. 8 | - Write or edit documentation. 9 | - Write specifications. 10 | - Write code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace). 11 | - Refactor code. 12 | - Write and/or work on [issues](https://github.com/ArielMAJ/front-back-integration-template/issues). 13 | - Review patches on [discussions](https://github.com/ArielMAJ/front-back-integration-template/discussions). 14 | - Be creative: any contribution is welcome! 15 | 16 | ## Submitting an issue 17 | 18 | This project uses the [GitHub issue tracker](https://github.com/ArielMAJ/front-back-integration-template/issues) to track bugs and features. 19 | Before submitting a bug report or feature request, check to make sure no one else has already submitted the same bug report. 20 | 21 | When submitting a bug report, please include any details that may be necessary to reproduce the bug, including the version of the project, Python/Node/Bun versions, and operating system. 22 | 23 | ## Writing code 24 | 25 | After you run `make install`, `pre-commit` will be installed. Make sure to always run the pre-commits before 26 | committing and making PRs. If you’re adding new functionality, it would be awesome if you could also provide tests and documentation to cover it, but don't let this stop you from contributing. 27 | 28 | ## Submitting a pull request 29 | 30 | 1. Fork the repository; 31 | 2. Create a topic branch (e.g. `feat/adding-xyz`, `fix/abc`, etc); 32 | 3. Implement your feature or bug fix; 33 | 4. Consider adding tests and/or documentation for your feature or bug fix; 34 | 5. Run `make pre-commit`; 35 | 6. Commit and push your changes to your branch; 36 | 7. Submit a pull request. 37 | 38 | ## License 39 | 40 | By contributing, you agree that your contributions will be licensed under its [MIT](./LICENSE) license. 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ariel Almeida 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq ("$(wildcard .env)","") 2 | include .env 3 | export 4 | endif 5 | 6 | API_DIR = backend 7 | VUE_FRONT_DIR = frontend 8 | 9 | MAKE_API = $(MAKE) -C $(API_DIR) 10 | MAKE_VUE_FRONT = $(MAKE) -C $(VUE_FRONT_DIR) 11 | 12 | run-api: 13 | $(MAKE_API) run 14 | 15 | run-vue: 16 | $(MAKE_VUE_FRONT) run 17 | 18 | install: 19 | $(MAKE_API) install 20 | $(MAKE_VUE_FRONT) install 21 | 22 | pre-commit: 23 | $(MAKE_API) pre-commit 24 | 25 | patch: pre-commit 26 | $(MAKE_API) patch 27 | $(MAKE_VUE_FRONT) patch 28 | 29 | minor: pre-commit 30 | $(MAKE_API) minor 31 | $(MAKE_VUE_FRONT) minor 32 | 33 | default-dot-envs: 34 | cp ./$(VUE_FRONT_DIR)/.env.example ./$(VUE_FRONT_DIR)/.env.local 35 | cp ./$(API_DIR)/.env.example ./$(API_DIR)/.env 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fullstack-vue-fastapi-template 2 | 3 | ## Table of Contents 4 | 5 | - [About](#about) 6 | - [Requirements to run the project](#requirements-to-run-the-project) 7 | - [How to run the project](#how-to-run-the-project) 8 | - [How to deploy](#how-to-deploy) 9 | - [Frontend](#frontend) 10 | - [Backend](#backend) 11 | - [Compatibility](#compatibility) 12 | - [Contributing](#contributing) 13 | - [Author](#author) 14 | - [License](#license) 15 | 16 | ## About 17 | 18 | A head start template for fullstack projects with integrated Vue front-end (bun/NodeJS) and FastAPI back-end (Python). 19 | 20 | ## Requirements to run the project 21 | 22 | - `make` 4.3; 23 | - `sudo apt install build-essential` on Ubuntu (build-essential might be needed for `pyenv`); or 24 | - `sudo apt install make` on Ubuntu; or 25 | - `brew install make` on macOS; or 26 | - `choco install make` on Windows; 27 | - Python 3.9.18; 28 | - Current Vercel supported version (but, if you're using more recent versions, it should probably work locally); 29 | - I personally recommend using [pyenv](https://github.com/pyenv/pyenv?tab=readme-ov-file#installation) for Python version management; 30 | - Node.js v20.11.0; 31 | - I personally recommend using [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating) for Node.js version management; 32 | - Bun 1.0.26: install [here](https://bun.sh/); 33 | 34 | ## How to run the project 35 | 36 | 1. Clone the repository with: 37 | 1. `git clone git@github.com:ArielMAJ/fullstack-vue-fastapi-template.git` (needs SSH key setup); or 38 | 2. `git clone https://github.com/ArielMAJ/fullstack-vue-fastapi-template.git` (no need for SSH key setup); 39 | 3. Download it if you don't have git and doesn't want to install it; 40 | 2. Change directory: `cd fullstack-vue-fastapi-template`; 41 | 3. Create `.env`'s from the `.env.example` files (you can just copy them as they are): 42 | ```bash 43 | make default-dot-envs 44 | ``` 45 | 4. Install the dependencies: 46 | ```bash 47 | make install 48 | ``` 49 | 5. Run the backend in a terminal: 50 | ```bash 51 | make run-api 52 | ``` 53 | 6. Run the frontend in another terminal: 54 | ```bash 55 | make run-vue 56 | ``` 57 | 7. Access the frontend at `http://localhost:8080/`. 58 | 8. Access the backend docs at `http://localhost:3000/docs` or `http://localhost:3000/redoc`. 59 | 60 | ## How to deploy 61 | 62 | ### Frontend 63 | 64 | There are many possible ways to deploy the frontend. If you're using just the front-end part of the template, I recommend bringing the front-end to the root of the repository, and using Vercel to deploy it. You can also use GitHub Pages to deploy it and this is my personal recommendation if you're using both front and back end in the same repository. 65 | 66 | To deploy with GitHub pages: 67 | 68 | 1. Access the equivalent of `https://github.com/ArielMAJ/fullstack-vue-fastapi-template/settings/pages` for your repository; 69 | 2. Select "deploy from a branch" and select `gh-pages` (create the branch if it doesn't exist); 70 | 71 | GitHub Pages will automatically deploy the frontend from the `gh-pages` branch of the repository every time something is pushed there. You can do this manually by running `bun run build` and pushing the `dist` folder to the `gh-pages` branch of the repository (this might be a little confusing to do). My recommendation is to make use of GitHub Actions to automate the process. The [auto_deploy.yml](./.github/workflows/auto_deploy.yml) file is already set up for this: 72 | 73 | Everytime you push to the `main` branch, the action will run and deploy the frontend to the `gh-pages` branch. You can check the status of the action in the "Actions" tab of the repository. If you want to use this action, you will need to set up the repository secrets and variables: 74 | 75 | 1. Repository secret `ACCESS_TOKEN` (access the equivalent of `https://github.com/ArielMAJ/fullstack-vue-fastapi-template/settings/secrets/actions` for your repository); 76 | ![GitHub secrets image](./docs/images/github-secrets.png) 77 | - You can generate a token [here](https://github.com/settings/tokens) and give it the `repo` scope; 78 | ![repo scope image](./docs/images/repo-scope-image.png) 79 | - This is needed for the `gh-pages` deployment (so the action can push to the `gh-pages` branch); 80 | 2. Repository variable `VITE_APP_BACKEND_ROOT_ENDPOINT` (access the equivalent of `https://github.com/ArielMAJ/fullstack-vue-fastapi-template/settings/variables/actions` for your repository); 81 | ![image](https://github.com/ArielMAJ/fullstack-vue-fastapi-template/assets/69123486/2b997bf8-1a79-4c6e-8596-55feb663f9b6) 82 | - Set the value to the root endpoint of your backend (e.g. `https://fullstack-vue-fastapi-template.vercel.app/`); 83 | - This is needed for the frontend to know where the backend is located, this way you can easily change the backend location without changing the code (e.g. development will point to `http://localhost:3000/` and production will point to the PROD backend); 84 | 85 | ### Backend 86 | 87 | I personally recommend Vercel for the backend, as it is the easiest to set up and use and it's free for personal projects. The [vercel.json](./vercel.json) file is already set up for this, but you're gonna need to change the root directory to your backend directory in Vercel: 88 | ![API root folder image](./docs/images/api-root-folder.png) 89 | 90 | Make sure to add any environment variables you create for your API in the Environment Variables section of the Vercel Project Settings. 91 | 92 | Obs.: The backend packages are locally managed with `poetry`, but, for simplicity in Vercel deployment, the `requirements.txt` file is present in the folder as well. You can generate the `requirements.txt` file with the command `poetry export -f requirements.txt --output requirements.txt --without-hashes` in the `backend` directory. If you use the pre-commit hook, it will automatically generate the `requirements.txt` file for you when you commit. 93 | 94 | ## Compatibility 95 | 96 | - Linux: works and is the primary development platform; 97 | - WSL: should work; 98 | - Windows: should work. The initial development stages were in Windows, but later Linux only; 99 | - MacOS: not tested. 100 | 101 | ## Contributing 102 | 103 | Any and everyone is welcome to test this tool locally and leave feedback in the discussions/issues page. If you have some free time and are interested in it, please do. I would love to hear your thoughts and suggestions. 104 | 105 | If you want to contribute to the project in any way, feel free to start issues, discussions and open pull requests. Check the [CONTRIBUTING](CONTRIBUTING.md) file for more information. 106 | 107 | ## Author 108 | 109 | - [@ArielMAJ](https://ariel.artadevs.tech/): ariel.maj@hotmail.com 110 | 111 | ## License 112 | 113 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 114 | -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | APPLICATION_HOST=127.0.0.1 2 | APPLICATION_PORT=3000 3 | 4 | APPLICATION_ROOT= 5 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /backend/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/python-poetry/poetry 3 | rev: "1.7.0" 4 | hooks: 5 | - id: poetry-check 6 | args: [--directory=backend] 7 | - id: poetry-lock 8 | args: [--directory=backend] 9 | - id: poetry-install 10 | args: [--directory=backend] 11 | - id: poetry-export 12 | args: 13 | [ 14 | "--directory=backend", 15 | "--without-hashes", 16 | "-o", 17 | "backend/requirements.txt", 18 | "-f", 19 | "requirements.txt", 20 | ] 21 | - repo: https://github.com/pre-commit/pre-commit-hooks 22 | rev: v4.5.0 23 | hooks: 24 | - id: trailing-whitespace 25 | - id: end-of-file-fixer 26 | exclude: ^.*\.egg-info/ 27 | - id: check-merge-conflict 28 | - id: check-case-conflict 29 | - id: check-json 30 | - id: check-toml 31 | exclude: tests/fixtures/invalid_lock/poetry\.lock 32 | - id: check-yaml 33 | - id: check-ast 34 | - id: debug-statements 35 | - id: check-docstring-first 36 | 37 | - repo: https://github.com/psf/black 38 | rev: 23.12.0 39 | hooks: 40 | - id: black 41 | language_version: python3.9 42 | args: 43 | [ 44 | --line-length=88, 45 | --target-version=py311, 46 | --exclude=\.venv/, 47 | --exclude=\.nox/, 48 | --exclude=\.tox/, 49 | --exclude=\.eggs/, 50 | --exclude=build/, 51 | --exclude=dist/, 52 | --exclude=docs/, 53 | --exclude=\.mypy_cache/, 54 | --exclude=\.pytest_cache/, 55 | --exclude=\.git/, 56 | --exclude=\.github/, 57 | --exclude=\.vscode/, 58 | --exclude=\.pyright/, 59 | --exclude=\.pylint_cache/, 60 | --exclude=\.hypothesis/, 61 | --exclude=\.mypy/, 62 | --exclude=\.coverage/, 63 | ] 64 | 65 | - repo: https://github.com/pycqa/flake8 66 | rev: 6.1.0 67 | hooks: 68 | - id: flake8 69 | args: [--max-line-length=88, --ignore=E203] 70 | 71 | - repo: https://github.com/myint/autoflake 72 | rev: v2.2.1 73 | hooks: 74 | - id: autoflake 75 | args: 76 | [ 77 | "--in-place", 78 | "--remove-all-unused-imports", 79 | "--remove-unused-variables", 80 | "--remove-duplicate-keys", 81 | ".", 82 | ] 83 | 84 | - repo: https://github.com/pycqa/isort 85 | rev: 5.13.2 86 | hooks: 87 | - id: isort 88 | args: [--filter-files, --profile=black, --src-path=src, --gitignore] 89 | 90 | - repo: https://github.com/pre-commit/mirrors-prettier 91 | rev: v4.0.0-alpha.6 92 | hooks: 93 | - id: prettier 94 | additional_dependencies: 95 | - prettier@2.8.1 96 | 97 | - repo: https://github.com/pre-commit/pre-commit 98 | rev: v3.6.0 99 | hooks: 100 | - id: validate_manifest 101 | 102 | - repo: https://github.com/astral-sh/ruff-pre-commit 103 | rev: v0.1.8 104 | hooks: 105 | - id: ruff 106 | -------------------------------------------------------------------------------- /backend/.python-version: -------------------------------------------------------------------------------- 1 | 3.9.18 -------------------------------------------------------------------------------- /backend/Makefile: -------------------------------------------------------------------------------- 1 | ifneq ("$(wildcard .env)","") 2 | include .env 3 | export 4 | endif 5 | 6 | 7 | run: 8 | poetry run python -m api 9 | 10 | test: 11 | ENVIRONMENT=test poetry run pytest --cov 12 | 13 | install: 14 | pip install poetry 15 | poetry install --no-root 16 | poetry lock 17 | poetry run pre-commit install 18 | 19 | pre-commit: 20 | poetry run pre-commit run --config ./.pre-commit-config.yaml 21 | 22 | patch: 23 | poetry version patch 24 | 25 | minor: 26 | poetry version minor 27 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # fastapi-backend-template 2 | 3 | A FastAPI backend template for a head start on new projects. 4 | 5 | See `Makefile` for examples on how to run the project. 6 | -------------------------------------------------------------------------------- /backend/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/api/__init__.py -------------------------------------------------------------------------------- /backend/api/__main__.py: -------------------------------------------------------------------------------- 1 | import uvicorn 2 | from api.config import Config 3 | 4 | 5 | def main() -> None: 6 | """Entrypoint of the application.""" 7 | uvicorn.run( 8 | "api.app:get_app", 9 | workers=Config.WORKERS_COUNT, 10 | host=Config.HOST, 11 | port=Config.PORT, 12 | reload=Config.RELOAD, 13 | log_level=Config.LOG_LEVEL.lower(), 14 | factory=True, 15 | ) 16 | 17 | 18 | if __name__ == "__main__": 19 | main() 20 | -------------------------------------------------------------------------------- /backend/api/app.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from api.entrypoints.router import api_router 4 | from fastapi import FastAPI 5 | from fastapi.middleware.cors import CORSMiddleware 6 | from fastapi.responses import JSONResponse 7 | from fastapi_cache import FastAPICache 8 | from fastapi_cache.backends.inmemory import InMemoryBackend 9 | 10 | APP_ROOT = Path(__file__).parent 11 | 12 | 13 | def get_app() -> FastAPI: 14 | """ 15 | Get FastAPI application. 16 | 17 | This is the main constructor of an application. 18 | 19 | :return: application. 20 | """ 21 | _app = FastAPI( 22 | title="fastapi-backend-template", 23 | default_response_class=JSONResponse, 24 | ) 25 | 26 | _app.add_middleware( 27 | CORSMiddleware, 28 | allow_origins=["*"], 29 | allow_credentials=True, 30 | allow_methods=["*"], 31 | allow_headers=["*"], 32 | ) 33 | _app.include_router(router=api_router) 34 | 35 | FastAPICache.init(InMemoryBackend(), prefix="fastapi-cache") 36 | return _app 37 | -------------------------------------------------------------------------------- /backend/api/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Application's and its environment's configuration. 3 | """ 4 | 5 | import os 6 | 7 | 8 | class Config: 9 | """Base configuration.""" 10 | 11 | ENVIRONMENT = os.getenv("ENVIRONMENT", "DEV") 12 | DEBUG = ENVIRONMENT == "DEV" 13 | TESTING = ENVIRONMENT == "TEST" 14 | 15 | HOST = os.getenv("APPLICATION_HOST", "127.0.0.1") 16 | PORT = int(os.getenv("APPLICATION_PORT", "3000")) 17 | WORKERS_COUNT = int(os.getenv("WORKERS_COUNT", "1")) 18 | RELOAD = os.getenv("RELOAD", "true").lower() == "true" 19 | LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") 20 | 21 | APPLICATION_ROOT = os.getenv("APPLICATION_ROOT", "") 22 | 23 | 24 | class TestConfig(Config): 25 | """Test configuration.""" 26 | 27 | ENVIRONMENT = "test" 28 | TESTING = True 29 | DEBUG = False 30 | PRESERVE_CONTEXT_ON_EXCEPTION = False 31 | -------------------------------------------------------------------------------- /backend/api/entrypoints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/api/entrypoints/__init__.py -------------------------------------------------------------------------------- /backend/api/entrypoints/monitoring/__init__.py: -------------------------------------------------------------------------------- 1 | """API for checking project status.""" 2 | from api.entrypoints.monitoring.views import router 3 | 4 | __all__ = ["router"] 5 | -------------------------------------------------------------------------------- /backend/api/entrypoints/monitoring/views.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | router = APIRouter() 4 | 5 | 6 | @router.get("/health") 7 | def health() -> None: 8 | """ 9 | Checks the health of the project. 10 | 11 | It returns 200 if the project is healthy. 12 | """ 13 | -------------------------------------------------------------------------------- /backend/api/entrypoints/router.py: -------------------------------------------------------------------------------- 1 | from fastapi.routing import APIRouter 2 | 3 | from api.entrypoints import monitoring, v1 4 | 5 | api_router = APIRouter() 6 | api_router.include_router(monitoring.router) 7 | api_router.include_router(v1.router, prefix="/v1", tags=["v1"]) 8 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/__init__.py: -------------------------------------------------------------------------------- 1 | """Router for version 1.""" 2 | from api.entrypoints.v1.router import router 3 | 4 | __all__ = ["router"] 5 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/random_number/__init__.py: -------------------------------------------------------------------------------- 1 | """Random number API.""" 2 | from api.entrypoints.v1.random_number.views import router 3 | 4 | __all__ = ["router"] 5 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/random_number/schema.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | 4 | class RandomResponse(BaseModel): 5 | """Random response model.""" 6 | 7 | message: float 8 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/random_number/views.py: -------------------------------------------------------------------------------- 1 | from api.entrypoints.v1.random_number.schema import RandomResponse 2 | from api.services.random_number_service import RandomNumberService 3 | from api.utils.custom_decorators import cache 4 | from fastapi import APIRouter 5 | 6 | router = APIRouter() 7 | 8 | 9 | @router.get("/", response_model=RandomResponse) 10 | @cache(expire=5) 11 | async def random_number(): 12 | """ 13 | Sends random number back to user. 14 | 15 | :returns: random number to user. 16 | """ 17 | return await RandomNumberService.get_random_number() 18 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/root_response/__init__.py: -------------------------------------------------------------------------------- 1 | """Root response API.""" 2 | from api.entrypoints.v1.root_response.views import router 3 | 4 | __all__ = ["router"] 5 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/root_response/schema.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | 4 | class RootResponse(BaseModel): 5 | message: str 6 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/root_response/views.py: -------------------------------------------------------------------------------- 1 | from api.entrypoints.v1.root_response.schema import RootResponse 2 | from api.services.root_response_service import RootResponseService 3 | from api.utils.custom_decorators import cache 4 | from fastapi import APIRouter 5 | 6 | router = APIRouter() 7 | 8 | 9 | @router.get("/", response_model=RootResponse) 10 | @cache(expire=5) 11 | async def root_response(): 12 | """ 13 | Sends root response back to user. 14 | 15 | :returns: root response to user. 16 | """ 17 | return await RootResponseService.get_root_response() 18 | -------------------------------------------------------------------------------- /backend/api/entrypoints/v1/router.py: -------------------------------------------------------------------------------- 1 | from fastapi.routing import APIRouter 2 | 3 | from api.entrypoints.v1 import root_response, random_number 4 | 5 | router = APIRouter() 6 | router.include_router(random_number.router, prefix="/random_number", tags=["Random Number"]) 7 | router.include_router(root_response.router, prefix="", tags=["Root Response"]) 8 | -------------------------------------------------------------------------------- /backend/api/services/random_number_service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Random number service class. 3 | """ 4 | 5 | 6 | import asyncio 7 | from random import random 8 | 9 | from api.entrypoints.v1.random_number.schema import RandomResponse 10 | from loguru import logger 11 | 12 | 13 | class RandomNumberService: 14 | """ 15 | Random number service class. 16 | """ 17 | 18 | @staticmethod 19 | async def get_random_number() -> RandomResponse: 20 | """ 21 | Get random number. 22 | 23 | :returns: random number. 24 | """ 25 | seconds = random() * 5 26 | logger.debug(f"Sleeping for {seconds} seconds before answering request.") 27 | await asyncio.sleep(seconds) 28 | return RandomResponse(message=round(seconds, 2)) 29 | -------------------------------------------------------------------------------- /backend/api/services/root_response_service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Root response service class. 3 | """ 4 | 5 | import asyncio 6 | from random import random 7 | 8 | from api.entrypoints.v1.root_response.schema import RootResponse 9 | from loguru import logger 10 | 11 | 12 | class RootResponseService: 13 | """ 14 | Root response service class. 15 | """ 16 | 17 | @staticmethod 18 | async def get_root_response() -> RootResponse: 19 | """ 20 | Get root response. 21 | 22 | :returns: root response. 23 | """ 24 | seconds = random() * 7 25 | logger.debug(f"Sleeping for {seconds} seconds before answering request.") 26 | await asyncio.sleep(seconds) 27 | return RootResponse(message="Hello World message from the back-end!") 28 | -------------------------------------------------------------------------------- /backend/api/utils/custom_decorators.py: -------------------------------------------------------------------------------- 1 | from api.config import Config, TestConfig 2 | from fastapi_cache.decorator import cache as fastapi_cache 3 | 4 | 5 | def ignore_cache(func): 6 | return func 7 | 8 | 9 | def cache(*args, **kwargs): 10 | if Config.ENVIRONMENT == TestConfig.ENVIRONMENT: 11 | return ignore_cache 12 | return fastapi_cache(*args, **kwargs) 13 | -------------------------------------------------------------------------------- /backend/api/vercel.py: -------------------------------------------------------------------------------- 1 | from api.app import get_app 2 | 3 | app = get_app() 4 | -------------------------------------------------------------------------------- /backend/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "fastapi-backend-template" 3 | version = "0.5.2" 4 | description = "A FastAPI backend template." 5 | authors = ["ArielMAJ "] 6 | readme = "README.md" 7 | license = "MIT" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.9" 11 | fastapi = "^0.109.2" 12 | uvicorn = {extras = ["standard"], version = "^0.27.1"} 13 | loguru = "^0.7.2" 14 | httpx = "^0.26.0" 15 | fastapi-cache2 = "^0.2.1" 16 | 17 | 18 | [tool.poetry.group.dev.dependencies] 19 | poetry-plugin-export = "^1.6.0" 20 | pre-commit = "^3.6.0" 21 | pytest = "^8.0.1" 22 | pytest-cov = "^4.1.0" 23 | coverage = "^7.4.1" 24 | pytest-asyncio = "^0.23.5" 25 | pytest-mock = "^3.12.0" 26 | 27 | [tool.coverage.run] 28 | branch=true 29 | source = ["api"] 30 | omit = [ 31 | "api/__main__.py", 32 | "api/config.py", 33 | ] 34 | 35 | [build-system] 36 | requires = ["poetry-core"] 37 | build-backend = "poetry.core.masonry.api" 38 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.5 ; python_version >= "3.11" and python_version < "4.0" 2 | aiosignal==1.3.1 ; python_version >= "3.11" and python_version < "4.0" 3 | annotated-types==0.7.0 ; python_version >= "3.9" and python_version < "4.0" 4 | anyio==4.4.0 ; python_version >= "3.9" and python_version < "4.0" 5 | attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" 6 | certifi==2024.7.4 ; python_version >= "3.9" and python_version < "4.0" 7 | click==8.1.7 ; python_version >= "3.9" and python_version < "4.0" 8 | colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows") 9 | exceptiongroup==1.2.2 ; python_version >= "3.9" and python_version < "3.11" 10 | fastapi-cache2==0.2.1 ; python_version >= "3.9" and python_version < "4.0" 11 | fastapi==0.109.2 ; python_version >= "3.9" and python_version < "4.0" 12 | frozenlist==1.4.1 ; python_version >= "3.11" and python_version < "4.0" 13 | h11==0.14.0 ; python_version >= "3.9" and python_version < "4.0" 14 | httpcore==1.0.5 ; python_version >= "3.9" and python_version < "4.0" 15 | httptools==0.6.1 ; python_version >= "3.9" and python_version < "4.0" 16 | httpx==0.26.0 ; python_version >= "3.9" and python_version < "4.0" 17 | idna==3.7 ; python_version >= "3.9" and python_version < "4.0" 18 | loguru==0.7.2 ; python_version >= "3.9" and python_version < "4.0" 19 | multidict==6.0.5 ; python_version >= "3.11" and python_version < "4.0" 20 | pendulum==3.0.0 ; python_version >= "3.9" and python_version < "4.0" 21 | pydantic-core==2.20.1 ; python_version >= "3.9" and python_version < "4.0" 22 | pydantic==2.8.2 ; python_version >= "3.9" and python_version < "4.0" 23 | python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "4.0" 24 | python-dotenv==1.0.1 ; python_version >= "3.9" and python_version < "4.0" 25 | pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "4.0" 26 | six==1.16.0 ; python_version >= "3.9" and python_version < "4.0" 27 | sniffio==1.3.1 ; python_version >= "3.9" and python_version < "4.0" 28 | starlette==0.36.3 ; python_version >= "3.9" and python_version < "4.0" 29 | typing-extensions==4.12.2 ; python_version >= "3.9" and python_version < "4.0" 30 | tzdata==2024.1 ; python_version >= "3.9" and python_version < "4.0" 31 | uvicorn==0.27.1 ; python_version >= "3.9" and python_version < "4.0" 32 | uvicorn[standard]==0.27.1 ; python_version >= "3.9" and python_version < "4.0" 33 | uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.9" and python_version < "4.0" 34 | watchfiles==0.22.0 ; python_version >= "3.9" and python_version < "4.0" 35 | websockets==12.0 ; python_version >= "3.9" and python_version < "4.0" 36 | win32-setctime==1.1.0 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "win32" 37 | yarl==1.9.4 ; python_version >= "3.11" and python_version < "4.0" 38 | -------------------------------------------------------------------------------- /backend/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/__init__.py -------------------------------------------------------------------------------- /backend/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from api.app import get_app 3 | from fastapi.testclient import TestClient 4 | 5 | 6 | @pytest.fixture(scope="function") 7 | def client(): 8 | """Create a TestClient instance for testing.""" 9 | return TestClient(get_app()) 10 | -------------------------------------------------------------------------------- /backend/tests/e2e/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/e2e/__init__.py -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/e2e/entrypoints/__init__.py -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/monitoring/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/e2e/entrypoints/monitoring/__init__.py -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/monitoring/test_health.py: -------------------------------------------------------------------------------- 1 | """End-to-end test for the health endpoint.""" 2 | 3 | 4 | def test_health(client): 5 | """Test the health endpoint.""" 6 | response = client.get("/health") 7 | assert response.status_code == 200 8 | assert response.json() is None 9 | -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/v1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/e2e/entrypoints/v1/__init__.py -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/v1/random_number/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/e2e/entrypoints/v1/random_number/__init__.py -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/v1/random_number/test_random_number_GET.py: -------------------------------------------------------------------------------- 1 | """End-to-end tests for the v1 root endpoint GET method.""" 2 | 3 | import pytest 4 | 5 | 6 | @pytest.mark.parametrize( 7 | "random_return_value", [0.1, 0.2, 0.3] 8 | ) # Specify different sleep times here 9 | def test_root_response_GET(client, mocker, random_return_value): 10 | """Test the endpoint.""" 11 | mock_sleep = mocker.patch("asyncio.sleep") 12 | mock_sleep.side_effect = lambda *args, **kwargs: None 13 | 14 | mock_random = mocker.patch("api.services.random_number_service.random") 15 | mock_random.return_value = random_return_value 16 | 17 | response = client.get("/v1/random_number/") 18 | assert response.status_code == 200 19 | assert response.json() == {"message": random_return_value * 5} 20 | mock_sleep.assert_called_once_with(random_return_value * 5) 21 | -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/v1/root_response/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/e2e/entrypoints/v1/root_response/__init__.py -------------------------------------------------------------------------------- /backend/tests/e2e/entrypoints/v1/root_response/test_root_response_GET.py: -------------------------------------------------------------------------------- 1 | """End-to-end tests for the v1 root endpoint GET method.""" 2 | 3 | import pytest 4 | 5 | 6 | @pytest.mark.parametrize( 7 | "random_return_value", [0.1, 0.2, 0.3] 8 | ) # Specify different sleep times here 9 | def test_root_response_GET(client, mocker, random_return_value): 10 | """Test the endpoint.""" 11 | mock_sleep = mocker.patch("asyncio.sleep") 12 | mock_sleep.side_effect = lambda *args, **kwargs: None 13 | 14 | mock_random = mocker.patch("api.services.root_response_service.random") 15 | mock_random.return_value = random_return_value 16 | 17 | response = client.get("/v1/") 18 | assert response.status_code == 200 19 | assert response.json() == {"message": "Hello World message from the back-end!"} 20 | mock_sleep.assert_called_once_with(random_return_value * 7) 21 | -------------------------------------------------------------------------------- /backend/tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/unit/__init__.py -------------------------------------------------------------------------------- /backend/tests/unit/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/backend/tests/unit/services/__init__.py -------------------------------------------------------------------------------- /backend/tests/unit/services/test_random_number_service.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the random number service.""" 2 | 3 | import pytest 4 | from api.entrypoints.v1.random_number.schema import RandomResponse 5 | from api.services.random_number_service import RandomNumberService 6 | 7 | 8 | @pytest.mark.asyncio 9 | @pytest.mark.parametrize( 10 | "random_return_value", [0.1, 0.2, 0.3] 11 | ) # Specify different sleep times here 12 | async def test_root_response_GET(client, mocker, random_return_value): 13 | """Test the endpoint.""" 14 | mock_sleep = mocker.patch("asyncio.sleep") 15 | mock_sleep.side_effect = lambda *args, **kwargs: None 16 | 17 | mock_random = mocker.patch("api.services.random_number_service.random") 18 | mock_random.return_value = random_return_value 19 | 20 | response = await RandomNumberService.get_random_number() 21 | 22 | assert isinstance(response, RandomResponse) 23 | assert response == RandomResponse(message=random_return_value * 5) 24 | assert response.message == random_return_value * 5 25 | mock_sleep.assert_called_once_with(random_return_value * 5) 26 | -------------------------------------------------------------------------------- /backend/tests/unit/services/test_root_response_service.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the root endpoint service.""" 2 | 3 | import pytest 4 | from api.entrypoints.v1.root_response.schema import RootResponse 5 | from api.services.root_response_service import RootResponseService 6 | 7 | 8 | @pytest.mark.asyncio 9 | @pytest.mark.parametrize( 10 | "random_return_value", [0.1, 0.2, 0.3] 11 | ) # Specify different sleep times here 12 | async def test_root_response_service(mocker, random_return_value): 13 | """Test the endpoint.""" 14 | mock_sleep = mocker.patch("asyncio.sleep") 15 | mock_sleep.side_effect = lambda *args, **kwargs: None 16 | 17 | mock_random = mocker.patch("api.services.root_response_service.random") 18 | mock_random.return_value = random_return_value 19 | 20 | response = await RootResponseService.get_root_response() 21 | 22 | assert isinstance(response, RootResponse) 23 | assert response == RootResponse(message="Hello World message from the back-end!") 24 | assert response.message == "Hello World message from the back-end!" 25 | mock_sleep.assert_called_once_with(random_return_value * 7) 26 | -------------------------------------------------------------------------------- /backend/tests/unit/test_vercel.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | def test_app_is_fastapi_app(): 5 | from api.vercel import app 6 | 7 | assert isinstance(app, FastAPI) 8 | assert app.title == "fastapi-backend-template" 9 | -------------------------------------------------------------------------------- /backend/tests/unit/utils/test_custom_decorators.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from api.utils.custom_decorators import cache, ignore_cache 3 | 4 | 5 | @pytest.mark.parametrize( 6 | "environment, expected, not_expected", 7 | [ 8 | ("test", ignore_cache, 1), 9 | ("dev", 1, ignore_cache), 10 | ("prd", 1, ignore_cache), 11 | ], 12 | ) 13 | def test_cache(mocker, environment, expected, not_expected): 14 | mock_config = mocker.patch("api.utils.custom_decorators.Config") 15 | mock_config.ENVIRONMENT = environment 16 | 17 | mock_fastapi_cache = mocker.patch("api.utils.custom_decorators.fastapi_cache") 18 | mock_fastapi_cache.return_value = 1 19 | 20 | assert cache() == expected 21 | assert cache() != not_expected 22 | -------------------------------------------------------------------------------- /backend/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "builds": [ 3 | { 4 | "src": "/api/vercel.py", 5 | "use": "@vercel/python" 6 | } 7 | ], 8 | "routes": [ 9 | { 10 | "src": "/(.*)", 11 | "dest": "/api/vercel.py" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /docs/images/api-root-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/docs/images/api-root-folder.png -------------------------------------------------------------------------------- /docs/images/github-secrets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/docs/images/github-secrets.png -------------------------------------------------------------------------------- /docs/images/repo-scope-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/docs/images/repo-scope-image.png -------------------------------------------------------------------------------- /frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | VITE_APP_BACKEND_ROOT_ENDPOINT=http://127.0.0.1:3000/ 2 | -------------------------------------------------------------------------------- /frontend/.eslintrc-auto-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "Component": true, 4 | "ComponentPublicInstance": true, 5 | "ComputedRef": true, 6 | "EffectScope": true, 7 | "ExtractDefaultPropTypes": true, 8 | "ExtractPropTypes": true, 9 | "ExtractPublicPropTypes": true, 10 | "InjectionKey": true, 11 | "PropType": true, 12 | "Ref": true, 13 | "VNode": true, 14 | "WritableComputedRef": true, 15 | "computed": true, 16 | "createApp": true, 17 | "customRef": true, 18 | "defineAsyncComponent": true, 19 | "defineComponent": true, 20 | "effectScope": true, 21 | "getCurrentInstance": true, 22 | "getCurrentScope": true, 23 | "h": true, 24 | "inject": true, 25 | "isProxy": true, 26 | "isReactive": true, 27 | "isReadonly": true, 28 | "isRef": true, 29 | "markRaw": true, 30 | "nextTick": true, 31 | "onActivated": true, 32 | "onBeforeMount": true, 33 | "onBeforeRouteLeave": true, 34 | "onBeforeRouteUpdate": true, 35 | "onBeforeUnmount": true, 36 | "onBeforeUpdate": true, 37 | "onDeactivated": true, 38 | "onErrorCaptured": true, 39 | "onMounted": true, 40 | "onRenderTracked": true, 41 | "onRenderTriggered": true, 42 | "onScopeDispose": true, 43 | "onServerPrefetch": true, 44 | "onUnmounted": true, 45 | "onUpdated": true, 46 | "provide": true, 47 | "reactive": true, 48 | "readonly": true, 49 | "ref": true, 50 | "resolveComponent": true, 51 | "shallowReactive": true, 52 | "shallowReadonly": true, 53 | "shallowRef": true, 54 | "toRaw": true, 55 | "toRef": true, 56 | "toRefs": true, 57 | "toValue": true, 58 | "triggerRef": true, 59 | "unref": true, 60 | "useAttrs": true, 61 | "useCssModule": true, 62 | "useCssVars": true, 63 | "useLink": true, 64 | "useRoute": true, 65 | "useRouter": true, 66 | "useSlots": true, 67 | "watch": true, 68 | "watchEffect": true, 69 | "watchPostEffect": true, 70 | "watchSyncEffect": true 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * .eslint.js 3 | * 4 | * ESLint configuration file. 5 | */ 6 | 7 | module.exports = { 8 | root: true, 9 | env: { 10 | node: true, 11 | }, 12 | extends: [ 13 | "plugin:vue/vue3-essential", 14 | "eslint:recommended", 15 | "@vue/eslint-config-typescript", 16 | ], 17 | rules: { 18 | "vue/multi-word-component-names": "off", 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /frontend/.nvmrc: -------------------------------------------------------------------------------- 1 | v20.11.0 2 | -------------------------------------------------------------------------------- /frontend/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | bun dev 3 | 4 | install: 5 | bun install 6 | 7 | patch: 8 | npm version patch 9 | 10 | minor: 11 | npm version minor 12 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Vuetify (Default) 2 | 3 | This is the official scaffolding tool for Vuetify, designed to give you a head start in building your new Vuetify application. It sets up a base template with all the necessary configurations and standard directory structure, enabling you to begin development without the hassle of setting up the project from scratch. 4 | 5 | ## ❗️ Important Links 6 | 7 | - 📄 [Docs](https://vuetifyjs.com/) 8 | - 🚨 [Issues](https://issues.vuetifyjs.com/) 9 | - 🏬 [Store](https://store.vuetifyjs.com/) 10 | - 🎮 [Playground](https://play.vuetifyjs.com/) 11 | - 💬 [Discord](https://community.vuetifyjs.com) 12 | 13 | ## 💿 Install 14 | 15 | Set up your project using your preferred package manager. Use the corresponding command to install the dependencies: 16 | 17 | | Package Manager | Command | 18 | | --------------------------------------------------------- | -------------- | 19 | | [yarn](https://yarnpkg.com/getting-started) | `yarn install` | 20 | | [npm](https://docs.npmjs.com/cli/v7/commands/npm-install) | `npm install` | 21 | | [pnpm](https://pnpm.io/installation) | `pnpm install` | 22 | | [bun](https://bun.sh/#getting-started) | `bun install` | 23 | 24 | After completing the installation, your environment is ready for Vuetify development. 25 | 26 | ## ✨ Features 27 | 28 | - 🖼️ **Optimized Front-End Stack**: Leverage the latest Vue 3 and Vuetify 3 for a modern, reactive UI development experience. [Vue 3](https://v3.vuejs.org/) | [Vuetify 3](https://vuetifyjs.com/en/) 29 | - 🚦 **Routing and Layouts**: Utilizes Vue Router for SPA navigation and vite-plugin-vue-layouts for organizing Vue file layouts. [Vue Router](https://router.vuejs.org/) | [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) 30 | - 💻 **Enhanced Development Experience**: Benefit from TypeScript's static type checking and the ESLint plugin suite for Vue, ensuring code quality and consistency. [TypeScript](https://www.typescriptlang.org/) | [ESLint Plugin Vue](https://eslint.vuejs.org/) 31 | - ⚡ **Next-Gen Tooling**: Powered by Vite, experience fast cold starts and instant HMR (Hot Module Replacement). [Vite](https://vitejs.dev/) 32 | - 🧩 **Automated Component Importing**: Streamline your workflow with unplugin-vue-components, automatically importing components as you use them. [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components) 33 | - 🛠️ **Strongly-Typed Vue**: Use vue-tsc for type-checking your Vue components, and enjoy a robust development experience. [vue-tsc](https://github.com/johnsoncodehk/volar/tree/master/packages/vue-tsc) 34 | 35 | These features are curated to provide a seamless development experience from setup to deployment, ensuring that your Vuetify application is both powerful and maintainable. 36 | 37 | ## 💡 Usage 38 | 39 | This section covers how to start the development server and build your project for production. 40 | 41 | ### Starting the Development Server 42 | 43 | To start the development server with hot-reload, run the following command. The server will be accessible at [http://localhost:8080](http://localhost:8080): 44 | 45 | ```bash 46 | bun dev 47 | ``` 48 | 49 | (Repeat for npm, pnpm, and yarn with respective commands.) 50 | 51 | > NODE_OPTIONS='--no-warnings' is added to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script. 52 | 53 | ### Building for Production 54 | 55 | To build your project for production, use: 56 | 57 | ```bash 58 | bun build 59 | ``` 60 | 61 | (Repeat for npm, pnpm, and yarn with respective commands.) 62 | 63 | Once the build process is completed, your application will be ready for deployment in a production environment. 64 | 65 | ## 💪 Support Vuetify Development 66 | 67 | This project is built with [Vuetify](https://vuetifyjs.com/en/), a UI Library with a comprehensive collection of Vue components. Vuetify is an MIT licensed Open Source project that has been made possible due to the generous contributions by our [sponsors and backers](https://vuetifyjs.com/introduction/sponsors-and-backers/). If you are interested in supporting this project, please consider: 68 | 69 | - [Requesting Enterprise Support](https://support.vuetifyjs.com/) 70 | - [Sponsoring John on Github](https://github.com/users/johnleider/sponsorship) 71 | - [Sponsoring Kael on Github](https://github.com/users/kaelwd/sponsorship) 72 | - [Supporting the team on Open Collective](https://opencollective.com/vuetify) 73 | - [Becoming a sponsor on Patreon](https://www.patreon.com/vuetify) 74 | - [Becoming a subscriber on Tidelift](https://tidelift.com/subscription/npm/vuetify) 75 | - [Making a one-time donation with Paypal](https://paypal.me/vuetify) 76 | 77 | ## 📑 License 78 | 79 | [MIT](http://opensource.org/licenses/MIT) 80 | 81 | Copyright (c) 2016-present Vuetify, LLC 82 | -------------------------------------------------------------------------------- /frontend/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | export {} 7 | declare global { 8 | const EffectScope: typeof import("vue")["EffectScope"]; 9 | const computed: typeof import("vue")["computed"]; 10 | const createApp: typeof import("vue")["createApp"]; 11 | const customRef: typeof import("vue")["customRef"]; 12 | const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"]; 13 | const defineComponent: typeof import("vue")["defineComponent"]; 14 | const effectScope: typeof import("vue")["effectScope"]; 15 | const getCurrentInstance: typeof import("vue")["getCurrentInstance"]; 16 | const getCurrentScope: typeof import("vue")["getCurrentScope"]; 17 | const h: typeof import("vue")["h"]; 18 | const inject: typeof import("vue")["inject"]; 19 | const isProxy: typeof import("vue")["isProxy"]; 20 | const isReactive: typeof import("vue")["isReactive"]; 21 | const isReadonly: typeof import("vue")["isReadonly"]; 22 | const isRef: typeof import("vue")["isRef"]; 23 | const markRaw: typeof import("vue")["markRaw"]; 24 | const nextTick: typeof import("vue")["nextTick"]; 25 | const onActivated: typeof import("vue")["onActivated"]; 26 | const onBeforeMount: typeof import("vue")["onBeforeMount"]; 27 | const onBeforeRouteLeave: typeof import("vue-router")["onBeforeRouteLeave"]; 28 | const onBeforeRouteUpdate: typeof import("vue-router")["onBeforeRouteUpdate"]; 29 | const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"]; 30 | const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"]; 31 | const onDeactivated: typeof import("vue")["onDeactivated"]; 32 | const onErrorCaptured: typeof import("vue")["onErrorCaptured"]; 33 | const onMounted: typeof import("vue")["onMounted"]; 34 | const onRenderTracked: typeof import("vue")["onRenderTracked"]; 35 | const onRenderTriggered: typeof import("vue")["onRenderTriggered"]; 36 | const onScopeDispose: typeof import("vue")["onScopeDispose"]; 37 | const onServerPrefetch: typeof import("vue")["onServerPrefetch"]; 38 | const onUnmounted: typeof import("vue")["onUnmounted"]; 39 | const onUpdated: typeof import("vue")["onUpdated"]; 40 | const provide: typeof import("vue")["provide"]; 41 | const reactive: typeof import("vue")["reactive"]; 42 | const readonly: typeof import("vue")["readonly"]; 43 | const ref: typeof import("vue")["ref"]; 44 | const resolveComponent: typeof import("vue")["resolveComponent"]; 45 | const shallowReactive: typeof import("vue")["shallowReactive"]; 46 | const shallowReadonly: typeof import("vue")["shallowReadonly"]; 47 | const shallowRef: typeof import("vue")["shallowRef"]; 48 | const toRaw: typeof import("vue")["toRaw"]; 49 | const toRef: typeof import("vue")["toRef"]; 50 | const toRefs: typeof import("vue")["toRefs"]; 51 | const toValue: typeof import("vue")["toValue"]; 52 | const triggerRef: typeof import("vue")["triggerRef"]; 53 | const unref: typeof import("vue")["unref"]; 54 | const useAttrs: typeof import("vue")["useAttrs"]; 55 | const useCssModule: typeof import("vue")["useCssModule"]; 56 | const useCssVars: typeof import("vue")["useCssVars"]; 57 | const useLink: typeof import("vue-router")["useLink"]; 58 | const useRoute: typeof import("vue-router")["useRoute"]; 59 | const useRouter: typeof import("vue-router")["useRouter"]; 60 | const useSlots: typeof import("vue")["useSlots"]; 61 | const watch: typeof import("vue")["watch"]; 62 | const watchEffect: typeof import("vue")["watchEffect"]; 63 | const watchPostEffect: typeof import("vue")["watchPostEffect"]; 64 | const watchSyncEffect: typeof import("vue")["watchSyncEffect"]; 65 | } 66 | // for type re-export 67 | declare global { 68 | // @ts-ignore 69 | export type { 70 | Component, 71 | ComponentPublicInstance, 72 | ComputedRef, 73 | ExtractDefaultPropTypes, 74 | ExtractPropTypes, 75 | ExtractPublicPropTypes, 76 | InjectionKey, 77 | PropType, 78 | Ref, 79 | VNode, 80 | WritableComputedRef, 81 | } from "vue"; 82 | import("vue"); 83 | } 84 | // for vue template auto import 85 | import { UnwrapRef } from "vue"; 86 | declare module "vue" { 87 | interface GlobalComponents {} 88 | interface ComponentCustomProperties { 89 | readonly EffectScope: UnwrapRef; 90 | readonly computed: UnwrapRef; 91 | readonly createApp: UnwrapRef; 92 | readonly customRef: UnwrapRef; 93 | readonly defineAsyncComponent: UnwrapRef< 94 | typeof import("vue")["defineAsyncComponent"] 95 | >; 96 | readonly defineComponent: UnwrapRef< 97 | typeof import("vue")["defineComponent"] 98 | >; 99 | readonly effectScope: UnwrapRef; 100 | readonly getCurrentInstance: UnwrapRef< 101 | typeof import("vue")["getCurrentInstance"] 102 | >; 103 | readonly getCurrentScope: UnwrapRef< 104 | typeof import("vue")["getCurrentScope"] 105 | >; 106 | readonly h: UnwrapRef; 107 | readonly inject: UnwrapRef; 108 | readonly isProxy: UnwrapRef; 109 | readonly isReactive: UnwrapRef; 110 | readonly isReadonly: UnwrapRef; 111 | readonly isRef: UnwrapRef; 112 | readonly markRaw: UnwrapRef; 113 | readonly nextTick: UnwrapRef; 114 | readonly onActivated: UnwrapRef; 115 | readonly onBeforeMount: UnwrapRef; 116 | readonly onBeforeRouteLeave: UnwrapRef< 117 | typeof import("vue-router")["onBeforeRouteLeave"] 118 | >; 119 | readonly onBeforeRouteUpdate: UnwrapRef< 120 | typeof import("vue-router")["onBeforeRouteUpdate"] 121 | >; 122 | readonly onBeforeUnmount: UnwrapRef< 123 | typeof import("vue")["onBeforeUnmount"] 124 | >; 125 | readonly onBeforeUpdate: UnwrapRef; 126 | readonly onDeactivated: UnwrapRef; 127 | readonly onErrorCaptured: UnwrapRef< 128 | typeof import("vue")["onErrorCaptured"] 129 | >; 130 | readonly onMounted: UnwrapRef; 131 | readonly onRenderTracked: UnwrapRef< 132 | typeof import("vue")["onRenderTracked"] 133 | >; 134 | readonly onRenderTriggered: UnwrapRef< 135 | typeof import("vue")["onRenderTriggered"] 136 | >; 137 | readonly onScopeDispose: UnwrapRef; 138 | readonly onServerPrefetch: UnwrapRef< 139 | typeof import("vue")["onServerPrefetch"] 140 | >; 141 | readonly onUnmounted: UnwrapRef; 142 | readonly onUpdated: UnwrapRef; 143 | readonly provide: UnwrapRef; 144 | readonly reactive: UnwrapRef; 145 | readonly readonly: UnwrapRef; 146 | readonly ref: UnwrapRef; 147 | readonly resolveComponent: UnwrapRef< 148 | typeof import("vue")["resolveComponent"] 149 | >; 150 | readonly shallowReactive: UnwrapRef< 151 | typeof import("vue")["shallowReactive"] 152 | >; 153 | readonly shallowReadonly: UnwrapRef< 154 | typeof import("vue")["shallowReadonly"] 155 | >; 156 | readonly shallowRef: UnwrapRef; 157 | readonly toRaw: UnwrapRef; 158 | readonly toRef: UnwrapRef; 159 | readonly toRefs: UnwrapRef; 160 | readonly toValue: UnwrapRef; 161 | readonly triggerRef: UnwrapRef; 162 | readonly unref: UnwrapRef; 163 | readonly useAttrs: UnwrapRef; 164 | readonly useCssModule: UnwrapRef; 165 | readonly useCssVars: UnwrapRef; 166 | readonly useLink: UnwrapRef; 167 | readonly useRoute: UnwrapRef; 168 | readonly useRouter: UnwrapRef; 169 | readonly useSlots: UnwrapRef; 170 | readonly watch: UnwrapRef; 171 | readonly watchEffect: UnwrapRef; 172 | readonly watchPostEffect: UnwrapRef< 173 | typeof import("vue")["watchPostEffect"] 174 | >; 175 | readonly watchSyncEffect: UnwrapRef< 176 | typeof import("vue")["watchSyncEffect"] 177 | >; 178 | } 179 | } 180 | declare module "@vue/runtime-core" { 181 | interface GlobalComponents {} 182 | interface ComponentCustomProperties { 183 | readonly EffectScope: UnwrapRef; 184 | readonly computed: UnwrapRef; 185 | readonly createApp: UnwrapRef; 186 | readonly customRef: UnwrapRef; 187 | readonly defineAsyncComponent: UnwrapRef< 188 | typeof import("vue")["defineAsyncComponent"] 189 | >; 190 | readonly defineComponent: UnwrapRef< 191 | typeof import("vue")["defineComponent"] 192 | >; 193 | readonly effectScope: UnwrapRef; 194 | readonly getCurrentInstance: UnwrapRef< 195 | typeof import("vue")["getCurrentInstance"] 196 | >; 197 | readonly getCurrentScope: UnwrapRef< 198 | typeof import("vue")["getCurrentScope"] 199 | >; 200 | readonly h: UnwrapRef; 201 | readonly inject: UnwrapRef; 202 | readonly isProxy: UnwrapRef; 203 | readonly isReactive: UnwrapRef; 204 | readonly isReadonly: UnwrapRef; 205 | readonly isRef: UnwrapRef; 206 | readonly markRaw: UnwrapRef; 207 | readonly nextTick: UnwrapRef; 208 | readonly onActivated: UnwrapRef; 209 | readonly onBeforeMount: UnwrapRef; 210 | readonly onBeforeRouteLeave: UnwrapRef< 211 | typeof import("vue-router")["onBeforeRouteLeave"] 212 | >; 213 | readonly onBeforeRouteUpdate: UnwrapRef< 214 | typeof import("vue-router")["onBeforeRouteUpdate"] 215 | >; 216 | readonly onBeforeUnmount: UnwrapRef< 217 | typeof import("vue")["onBeforeUnmount"] 218 | >; 219 | readonly onBeforeUpdate: UnwrapRef; 220 | readonly onDeactivated: UnwrapRef; 221 | readonly onErrorCaptured: UnwrapRef< 222 | typeof import("vue")["onErrorCaptured"] 223 | >; 224 | readonly onMounted: UnwrapRef; 225 | readonly onRenderTracked: UnwrapRef< 226 | typeof import("vue")["onRenderTracked"] 227 | >; 228 | readonly onRenderTriggered: UnwrapRef< 229 | typeof import("vue")["onRenderTriggered"] 230 | >; 231 | readonly onScopeDispose: UnwrapRef; 232 | readonly onServerPrefetch: UnwrapRef< 233 | typeof import("vue")["onServerPrefetch"] 234 | >; 235 | readonly onUnmounted: UnwrapRef; 236 | readonly onUpdated: UnwrapRef; 237 | readonly provide: UnwrapRef; 238 | readonly reactive: UnwrapRef; 239 | readonly readonly: UnwrapRef; 240 | readonly ref: UnwrapRef; 241 | readonly resolveComponent: UnwrapRef< 242 | typeof import("vue")["resolveComponent"] 243 | >; 244 | readonly shallowReactive: UnwrapRef< 245 | typeof import("vue")["shallowReactive"] 246 | >; 247 | readonly shallowReadonly: UnwrapRef< 248 | typeof import("vue")["shallowReadonly"] 249 | >; 250 | readonly shallowRef: UnwrapRef; 251 | readonly toRaw: UnwrapRef; 252 | readonly toRef: UnwrapRef; 253 | readonly toRefs: UnwrapRef; 254 | readonly toValue: UnwrapRef; 255 | readonly triggerRef: UnwrapRef; 256 | readonly unref: UnwrapRef; 257 | readonly useAttrs: UnwrapRef; 258 | readonly useCssModule: UnwrapRef; 259 | readonly useCssVars: UnwrapRef; 260 | readonly useLink: UnwrapRef; 261 | readonly useRoute: UnwrapRef; 262 | readonly useRouter: UnwrapRef; 263 | readonly useSlots: UnwrapRef; 264 | readonly watch: UnwrapRef; 265 | readonly watchEffect: UnwrapRef; 266 | readonly watchPostEffect: UnwrapRef< 267 | typeof import("vue")["watchPostEffect"] 268 | >; 269 | readonly watchSyncEffect: UnwrapRef< 270 | typeof import("vue")["watchSyncEffect"] 271 | >; 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /frontend/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/frontend/bun.lockb -------------------------------------------------------------------------------- /frontend/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | export {} 7 | 8 | declare module "vue" { 9 | export interface GlobalComponents { 10 | copy: typeof import("./src/components/loaders/Roller copy.vue")["default"]; 11 | Dots: typeof import("./src/components/loaders/Dots.vue")["default"]; 12 | Dots2: typeof import("./src/components/loaders/Dots2.vue")["default"]; 13 | DualRing: typeof import("./src/components/loaders/DualRing.vue")["default"]; 14 | Ellipsis: typeof import("./src/components/loaders/Ellipsis.vue")["default"]; 15 | Ellipsis2: typeof import("./src/components/loaders/Ellipsis2.vue")["default"]; 16 | HelloWorld: typeof import("./src/components/HelloWorld.vue")["default"]; 17 | Ring: typeof import("./src/components/loaders/Ring.vue")["default"]; 18 | Roller: typeof import("./src/components/loaders/Roller.vue")["default"]; 19 | RouterLink: typeof import("vue-router")["RouterLink"]; 20 | RouterView: typeof import("vue-router")["RouterView"]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ARTA Template 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuetify-frontend-template", 3 | "version": "0.5.2", 4 | "scripts": { 5 | "dev": "cross-env NODE_OPTIONS='--no-warnings' vite", 6 | "build": "vite build", 7 | "preview": "vite preview", 8 | "lint": "eslint . --fix --ignore-path .gitignore" 9 | }, 10 | "dependencies": { 11 | "@mdi/font": "7.0.96", 12 | "core-js": "^3.34.0", 13 | "roboto-fontface": "*", 14 | "vue": "^3.3.0", 15 | "vuetify": "^3.0.0" 16 | }, 17 | "devDependencies": { 18 | "@babel/types": "^7.23.0", 19 | "@types/node": "^20.10.0", 20 | "@vitejs/plugin-vue": "^4.5.0", 21 | "@vue/eslint-config-typescript": "^12.0.0", 22 | "cross-env": "^7.0.3", 23 | "eslint": "^8.56.0", 24 | "eslint-config-standard": "^17.1.0", 25 | "eslint-plugin-import": "^2.29.0", 26 | "eslint-plugin-n": "^16.4.0", 27 | "eslint-plugin-node": "^11.1.0", 28 | "eslint-plugin-promise": "^6.1.1", 29 | "eslint-plugin-vue": "^9.19.0", 30 | "pinia": "^2.1.7", 31 | "sass": "^1.69.0", 32 | "typescript": "^5.3.0", 33 | "unplugin-auto-import": "^0.17.3", 34 | "unplugin-fonts": "^1.1.0", 35 | "unplugin-vue-components": "^0.26.0", 36 | "unplugin-vue-router": "^0.7.0", 37 | "vite": "^5.0.0", 38 | "vite-plugin-vue-layouts": "^0.10.0", 39 | "vite-plugin-vuetify": "^2.0.0", 40 | "vue-router": "^4.2.0", 41 | "vue-tsc": "^1.8.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArielMAJ/fullstack-vue-fastapi-template/6495e8669a8bbe4be83bc8cfe8202dc80de03aaa/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 75 | 76 | 124 | -------------------------------------------------------------------------------- /frontend/src/components/README.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | Vue template files in this folder are automatically imported. 4 | 5 | ## 🚀 Usage 6 | 7 | Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it. 8 | 9 | The following example assumes a component located at `src/components/MyComponent.vue`: 10 | 11 | ```vue 12 | 17 | 18 | 21 | ``` 22 | 23 | When your template is rendered, the component's import will automatically be inlined, which renders to this: 24 | 25 | ```vue 26 | 31 | 32 | 35 | ``` 36 | -------------------------------------------------------------------------------- /frontend/src/components/loaders/DualRing.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 33 | -------------------------------------------------------------------------------- /frontend/src/components/loaders/Ellipsis.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 69 | -------------------------------------------------------------------------------- /frontend/src/components/loaders/Ellipsis2.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 52 | -------------------------------------------------------------------------------- /frontend/src/components/loaders/Ring.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 49 | -------------------------------------------------------------------------------- /frontend/src/components/loaders/Roller.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 102 | -------------------------------------------------------------------------------- /frontend/src/layouts/README.md: -------------------------------------------------------------------------------- 1 | # Layouts 2 | 3 | Layouts are reusable components that wrap around pages. They are used to provide a consistent look and feel across multiple pages. 4 | 5 | Full documentation for this feature can be found in the Official [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) repository. 6 | -------------------------------------------------------------------------------- /frontend/src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /frontend/src/layouts/default/AppBar.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /frontend/src/layouts/default/View.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * main.ts 3 | * 4 | * Bootstraps Vuetify and other plugins then mounts the App` 5 | */ 6 | 7 | // Plugins 8 | import { registerPlugins } from "@/plugins"; 9 | 10 | // Components 11 | import App from "./App.vue"; 12 | 13 | // Composables 14 | import { createApp } from "vue"; 15 | 16 | const app = createApp(App); 17 | 18 | registerPlugins(app); 19 | 20 | app.mount("#app"); 21 | -------------------------------------------------------------------------------- /frontend/src/pages/README.md: -------------------------------------------------------------------------------- 1 | # Pages 2 | 3 | Vue components created in this folder will automatically be converted to navigatable routes. 4 | 5 | Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository. 6 | -------------------------------------------------------------------------------- /frontend/src/pages/double-random.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /frontend/src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /frontend/src/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally. 4 | -------------------------------------------------------------------------------- /frontend/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.ts 3 | * 4 | * Automatically included in `./src/main.ts` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from "./vuetify"; 9 | import pinia from "../store"; 10 | import router from "../router"; 11 | 12 | // Types 13 | import type { App } from "vue"; 14 | 15 | export function registerPlugins(app: App) { 16 | app.use(vuetify).use(router).use(pinia); 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/vuetify.ts 3 | * 4 | * Framework documentation: https://vuetifyjs.com` 5 | */ 6 | 7 | // Styles 8 | import "@mdi/font/css/materialdesignicons.css"; 9 | import "vuetify/styles"; 10 | 11 | // Composables 12 | import { createVuetify } from "vuetify"; 13 | 14 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 15 | export default createVuetify({ 16 | // 17 | }); 18 | -------------------------------------------------------------------------------- /frontend/src/router/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * router/index.ts 3 | * 4 | * Automatic routes for `./src/pages/*.vue` 5 | */ 6 | 7 | // Composables 8 | import { createRouter, createWebHistory } from "vue-router/auto"; 9 | import { setupLayouts } from "virtual:generated-layouts"; 10 | 11 | const router = createRouter({ 12 | history: createWebHistory(process.env.BASE_URL), 13 | extendRoutes: setupLayouts, 14 | }); 15 | 16 | export default router; 17 | -------------------------------------------------------------------------------- /frontend/src/store/README.md: -------------------------------------------------------------------------------- 1 | # Store 2 | 3 | Pinia stores are used to store reactive state and expose actions to mutate it. 4 | 5 | Full documentation for this feature can be found in the Official [Pinia](https://pinia.esm.dev/) repository. 6 | -------------------------------------------------------------------------------- /frontend/src/store/app.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { defineStore } from "pinia"; 3 | 4 | export const useAppStore = defineStore("app", { 5 | state: () => ({ 6 | // 7 | }), 8 | }); 9 | -------------------------------------------------------------------------------- /frontend/src/store/index.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { createPinia } from "pinia"; 3 | 4 | export default createPinia(); 5 | -------------------------------------------------------------------------------- /frontend/src/styles/README.md: -------------------------------------------------------------------------------- 1 | # Styles 2 | 3 | This directory is for configuring the styles of the application. 4 | -------------------------------------------------------------------------------- /frontend/src/styles/settings.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * src/styles/settings.scss 3 | * 4 | * Configures SASS variables and Vuetify overwrites 5 | */ 6 | 7 | // https://vuetifyjs.com/features/sass-variables/` 8 | // @use 'vuetify/settings' with ( 9 | // $color-pack: false 10 | // ); 11 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "*.vue" { 4 | import type { DefineComponent } from "vue"; 5 | const component: DefineComponent<{}, {}, any>; 6 | export default component; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "jsx": "preserve", 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["ESNext", "DOM"], 14 | "skipLibCheck": true, 15 | "noEmit": true, 16 | "paths": { 17 | "@/*": ["src/*"] 18 | } 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 21 | "references": [{ "path": "./tsconfig.node.json" }], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.mts"] 9 | } 10 | -------------------------------------------------------------------------------- /frontend/typed-router.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️ 5 | // It's recommended to commit this file. 6 | // Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. 7 | 8 | /// 9 | 10 | import type { 11 | // type safe route locations 12 | RouteLocationTypedList, 13 | RouteLocationResolvedTypedList, 14 | RouteLocationNormalizedTypedList, 15 | RouteLocationNormalizedLoadedTypedList, 16 | RouteLocationAsString, 17 | RouteLocationAsRelativeTypedList, 18 | RouteLocationAsPathTypedList, 19 | 20 | // helper types 21 | // route definitions 22 | RouteRecordInfo, 23 | ParamValue, 24 | ParamValueOneOrMore, 25 | ParamValueZeroOrMore, 26 | ParamValueZeroOrOne, 27 | 28 | // vue-router extensions 29 | _RouterTyped, 30 | RouterLinkTyped, 31 | RouterLinkPropsTyped, 32 | NavigationGuard, 33 | UseLinkFnTyped, 34 | 35 | // data fetching 36 | _DataLoader, 37 | _DefineLoaderOptions, 38 | } from 'unplugin-vue-router/types' 39 | 40 | declare module "vue-router/auto/routes" { 41 | export interface RouteNamedMap { 42 | "/": RouteRecordInfo<"/", "/", Record, Record>; 43 | "/double-random": RouteRecordInfo< 44 | "/double-random", 45 | "/double-random", 46 | Record, 47 | Record 48 | >; 49 | } 50 | } 51 | 52 | declare module "vue-router/auto" { 53 | import type { RouteNamedMap } from "vue-router/auto/routes"; 54 | 55 | export type RouterTyped = _RouterTyped; 56 | 57 | /** 58 | * Type safe version of `RouteLocationNormalized` (the type of `to` and `from` in navigation guards). 59 | * Allows passing the name of the route to be passed as a generic. 60 | */ 61 | export type RouteLocationNormalized< 62 | Name extends keyof RouteNamedMap = keyof RouteNamedMap 63 | > = RouteLocationNormalizedTypedList[Name]; 64 | 65 | /** 66 | * Type safe version of `RouteLocationNormalizedLoaded` (the return type of `useRoute()`). 67 | * Allows passing the name of the route to be passed as a generic. 68 | */ 69 | export type RouteLocationNormalizedLoaded< 70 | Name extends keyof RouteNamedMap = keyof RouteNamedMap 71 | > = RouteLocationNormalizedLoadedTypedList[Name]; 72 | 73 | /** 74 | * Type safe version of `RouteLocationResolved` (the returned route of `router.resolve()`). 75 | * Allows passing the name of the route to be passed as a generic. 76 | */ 77 | export type RouteLocationResolved< 78 | Name extends keyof RouteNamedMap = keyof RouteNamedMap 79 | > = RouteLocationResolvedTypedList[Name]; 80 | 81 | /** 82 | * Type safe version of `RouteLocation` . Allows passing the name of the route to be passed as a generic. 83 | */ 84 | export type RouteLocation< 85 | Name extends keyof RouteNamedMap = keyof RouteNamedMap 86 | > = RouteLocationTypedList[Name]; 87 | 88 | /** 89 | * Type safe version of `RouteLocationRaw` . Allows passing the name of the route to be passed as a generic. 90 | */ 91 | export type RouteLocationRaw< 92 | Name extends keyof RouteNamedMap = keyof RouteNamedMap 93 | > = 94 | | RouteLocationAsString 95 | | RouteLocationAsRelativeTypedList[Name] 96 | | RouteLocationAsPathTypedList[Name]; 97 | 98 | /** 99 | * Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. 100 | */ 101 | export type RouteParams = 102 | RouteNamedMap[Name]["params"]; 103 | /** 104 | * Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. 105 | */ 106 | export type RouteParamsRaw = 107 | RouteNamedMap[Name]["paramsRaw"]; 108 | 109 | export function useRouter(): RouterTyped; 110 | export function useRoute< 111 | Name extends keyof RouteNamedMap = keyof RouteNamedMap 112 | >(name?: Name): RouteLocationNormalizedLoadedTypedList[Name]; 113 | 114 | export const useLink: UseLinkFnTyped; 115 | 116 | export function onBeforeRouteLeave( 117 | guard: NavigationGuard 118 | ): void; 119 | export function onBeforeRouteUpdate( 120 | guard: NavigationGuard 121 | ): void; 122 | 123 | export const RouterLink: RouterLinkTyped; 124 | export const RouterLinkProps: RouterLinkPropsTyped; 125 | 126 | // Experimental Data Fetching 127 | 128 | export function defineLoader< 129 | P extends Promise, 130 | Name extends keyof RouteNamedMap = keyof RouteNamedMap, 131 | isLazy extends boolean = false 132 | >( 133 | name: Name, 134 | loader: (route: RouteLocationNormalizedLoaded) => P, 135 | options?: _DefineLoaderOptions 136 | ): _DataLoader, isLazy>; 137 | export function defineLoader< 138 | P extends Promise, 139 | isLazy extends boolean = false 140 | >( 141 | loader: (route: RouteLocationNormalizedLoaded) => P, 142 | options?: _DefineLoaderOptions 143 | ): _DataLoader, isLazy>; 144 | 145 | export { 146 | _definePage as definePage, 147 | _HasDataLoaderMeta as HasDataLoaderMeta, 148 | _setupDataFetchingGuard as setupDataFetchingGuard, 149 | _stopDataFetchingScope as stopDataFetchingScope, 150 | } from "unplugin-vue-router/runtime"; 151 | } 152 | 153 | declare module "vue-router" { 154 | import type { RouteNamedMap } from "vue-router/auto/routes"; 155 | 156 | export interface TypesConfig { 157 | beforeRouteUpdate: NavigationGuard; 158 | beforeRouteLeave: NavigationGuard; 159 | 160 | $route: RouteLocationNormalizedLoadedTypedList[keyof RouteNamedMap]; 161 | $router: _RouterTyped; 162 | 163 | RouterLink: RouterLinkTyped; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /frontend/vite.config.mts: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import AutoImport from "unplugin-auto-import/vite"; 3 | import Components from "unplugin-vue-components/vite"; 4 | import Fonts from "unplugin-fonts/vite"; 5 | import Layouts from "vite-plugin-vue-layouts"; 6 | import Vue from "@vitejs/plugin-vue"; 7 | import VueRouter from "unplugin-vue-router/vite"; 8 | import Vuetify, { transformAssetUrls } from "vite-plugin-vuetify"; 9 | 10 | // Utilities 11 | import { defineConfig } from "vite"; 12 | import { fileURLToPath, URL } from "node:url"; 13 | 14 | // https://vitejs.dev/config/ 15 | export default defineConfig({ 16 | plugins: [ 17 | VueRouter(), 18 | Layouts(), 19 | Vue({ 20 | template: { transformAssetUrls }, 21 | }), 22 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 23 | Vuetify({ 24 | autoImport: true, 25 | styles: { 26 | configFile: "src/styles/settings.scss", 27 | }, 28 | }), 29 | Components(), 30 | Fonts({ 31 | google: { 32 | families: [ 33 | { 34 | name: "Roboto", 35 | styles: "wght@100;300;400;500;700;900", 36 | }, 37 | ], 38 | }, 39 | }), 40 | AutoImport({ 41 | imports: ["vue", "vue-router"], 42 | dts: true, 43 | eslintrc: { 44 | enabled: true, 45 | }, 46 | vueTemplate: true, 47 | }), 48 | ], 49 | define: { "process.env": {} }, 50 | resolve: { 51 | alias: { 52 | "@": fileURLToPath(new URL("./src", import.meta.url)), 53 | }, 54 | extensions: [".js", ".json", ".jsx", ".mjs", ".ts", ".tsx", ".vue"], 55 | }, 56 | server: { 57 | port: 8080, 58 | }, 59 | }); 60 | --------------------------------------------------------------------------------