A cookiecutter template for bootstrapping a FastAPI and React project using a modern stack.
It is often laborsome to start a new project. 90% of the time you have to decide how to handle authentication, reverse proxies, docker containers, testing, server-side validation, linting, etc. before you can even get started.
FastAPI-React serves to streamline and give you that functionality out of the box.
It is meant as a lightweight/React alternative to FastAPI's official fullstack project. If you want a more comprehensive project in Vue, I would suggest you start there. A lot of the backend code is taken from that project or the FastAPI official docs.
First, install cookiecutter if you don't already have it:
bash
pip3 install cookiecutter
Second, install docker-compose if you don't already have it:
docker-compose installation official docs.
Then, in the directory you want your project to live:
bash
cookiecutter gh:Buuntu/fastapi-react
You will need to put in a few variables and it will create a project directory
(called whatever you set for project_slug
).
Change into your project directory and run:
bash
chmod +x scripts/build.sh
./scripts/build.sh
This will build and run the docker containers, run the alembic migrations, and load the initial data (a test user).
It may take a while to build the first time it's run since it needs to fetch all the docker images.
Once you've built the images once, you can simply use regular docker-compose
commands to manage your development environment, for example to start your
containers:
bash
docker-compose up -d
Once this finishes you can navigate to the port set during setup (default is
localhost:8000
), you should see the slightly modified create-react-app page:
Note: If you see an Nginx error at first with a 502: Bad Gateway
page, you
may have to wait for webpack to build the development server (the nginx
container builds much more quickly).
Login screen:
The backend docs will be at http://localhost:8000/api/docs
.
This project uses react-admin for a highly configurable admin dashboard.
After starting the project, navigate to http://localhost:8000/admin
. You
should see a login screen. Use the username/password you set for the superuser
on project setup.
NOTE: regular users will not be able to access the admin dashboard
You should now see a list of users which you can edit, add, and delete. The
table is configured with the REST endpoints to the FastAPI /users
routes in
the backend.
The admin dashboard is kept in the frontend/src/admin
directory to keep it
separate from the regular frontend.
To generate a secure key used for encrypting/decrypting the JSON Web Tokens, you can run this command:
bash
openssl rand -hex 32
The default is fine for development but you will want something more secure for production.
You can either set this on project setup as secret_key
or manually edit the
Python SECRET_KEY
variable in backend/app/core/security.py
.
This project comes with Pytest and a few Pytest fixtures for easier mocking. The
fixtures are all located in backend/conftest.py
within your project directory.
All tests are configured to run on a test database using SQLAlchemy transactions to reset the testing state on each function. This is to avoid a database call affecting the state of a different test.
These fixtures are included in backend/conftest.py
and are automatically
imported into any test files that being with test_
.
The test_db
fixture is an empty test database and an instance of a SQLAlchemy
Session class.
python
def test_user(test_db):
assert test_db.query(models.User).all()
python
def test_user_exists(test_user):
assert test_user.email == "[email protected]"
python
def test_superuser(client, test_superuser):
assert test_superuser.is_superuser
To use an unauthenticated test client, use client
:
python
def test_get_users(client):
client.get("/api/v1/users")
assert response.status_code == 200
If you need an authenticated client using OAuth2 and JWTs:
python
def test_user_me(client, user_token_headers):
response = client.get(
"/api/v1/users/me",
headers=user_token_headers,
)
assert response.status_code == 200
Since OAuth2 expects the access token in the headers, you will need to pass in
user_token_headers
as the headers
argument in any client request that
requires authentication.
python
def test_user_me(client, superuser_token_headers):
response = client.get(
"/api/v1/users",
headers=superuser_token_headers,
)
assert response.status_code == 200
This template comes with Celery and Redis Docker containers pre-configured for you. For any long running processes, it's recommended that you handle these using a task queue like Celery to avoid making the client wait for a request to finish. Some examples of this might be sending emails, uploading large files, or any long running, resource intensive tasks.
There is an example task in backend/app/tasks.py
and an example Celery test in
backend/app/tests/test_tasks.py
. This test runs synchronously, which is what
Celery docs recommend.
If you are not happy with Celery or Redis, it should be easy to swap these containers out with your favorite tools. Some suggested alternatives might be Huey as the task queue and RabbitMQ for the message broker.
You can monitor tasks using Flower by going to http://localhost:5555
There are a few helper methods to handle authentication in frontend/src/utils
.
These store and access the JWT returned by FastAPI in local storage. Even though
this doesn't add any security, we prevent loading routes that might be protected
on the frontend, which results in a better UX experience.
```typescript // in src/utils/auth.ts
/ * Handles authentication with backend and stores in JWT in local storage / const login = (email: string, password: string) => boolean; ```
```typescript // in src/utils/auth.ts
// clears token from local storage const logout = (email: string, password: string) => void; ```
typescript
// Checks authenticated state from JWT tokens
const isAuthenticated = () => boolean;
Some basic routes are included (and handled in frontend/Routes.tsx
).
/login
- Login screen/logout
- Logout/
- Home/protected
- Example of protected routeThis handles routes that require authentication. It will automatically check whether the correct token with the "user" permissions is present or redirect to the home page.
```JSX // in src/Routes.tsx import { Switch } from 'react-router-dom';
// Replace this with your component import { ProtectedComponent } from 'components';
const Routes = () => (
This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it may be easiest to use Docker in Swarm Mode with an Nginx main load balancer proxy handling automatic HTTPS certificates, using the ideas from DockerSwarm.rocks.
Please refer to DockerSwarm.rocks to see how to deploy such a cluster easily. You will have to change the Traefik examples to Nginx or update your docker-compose file.
Contributing is more than welcome. Please read the Contributing doc to find out more.
Bumps ipython from 7.31.1 to 8.10.0.
15ea1ed
release 8.10.0560ad10
DOC: Update what's new for 8.10 (#13939)7557ade
DOC: Update what's new for 8.10385d693
Merge pull request from GHSA-29gw-9793-fvw7e548ee2
Swallow potential exceptions from showtraceback() (#13934)0694b08
MAINT: mock slowest test. (#13885)8655912
MAINT: mock slowest test.a011765
Isolate the attack tests with setUp and tearDown methodsc7a9470
Add some regression tests for this changefd34cf5
Swallow potential exceptions from showtraceback()Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
There is some issue with types resolution
/app/src/index.tsx TypeScript error in /app/src/index.tsx(8,4): 'Router' cannot be used as a JSX component. Its instance type 'BrowserRouter' is not a valid JSX element. The types returned by 'render()' are incompatible between these types. Type 'React.ReactNode' is not assignable to type 'import("/node_modules/@types/react-transition-group/node_modules/@types/react/index").ReactNode'. TS2786
6 |
7 | ReactDOM.render(
8 |
| ^ 9 | , 11 | document.getElementById('root')10 |
After a clean install and trying to run this, the frontend locks up and I get the following errors:
``
Warning: React does not recognize the
computedMatchprop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase
computedmatch` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
ReferenceError: Can't find variable: process
TypeScript error in /app/src/index.tsx(8,4): 'Router' cannot be used as a JSX component. Its instance type 'BrowserRouter' is not a valid JSX element. The types returned by 'render()' are incompatible between these types. Type 'React.ReactNode' is not assignable to type 'import("/node_modules/@types/react-transition-group/node_modules/@types/react/index").ReactNode'. TS2786
6 |
7 | ReactDOM.render(
8 |
| ^ 9 | , 11 | document.getElementById('root')10 |
TypeScript error in /app/src/Routes.tsx(31,6): 'Switch' cannot be used as a JSX component. Its instance type 'Switch' is not a valid JSX element. The types returned by 'render()' are incompatible between these types. Type 'React.ReactNode' is not assignable to type 'import("/node_modules/@types/react-transition-group/node_modules/@types/react/index").ReactNode'. TS2786
TypeScript error in /app/src/views/SignUp.tsx(58,6): 'Redirect' cannot be used as a JSX component. Its instance type 'Redirect' is not a valid JSX element. TS2786 ```
Any idea how to troubleshoot this?
Frontend build faild, don't know why
bash
/app/run.sh: line 2: $'\r': command not found
/app/run.sh: line 3: syntax error near unexpected token `$'in\r''
'app/run.sh: line 3: `case $1 in
Bumps httpx from 0.15.5 to 0.23.0.
Sourced from httpx's releases.
Version 0.23.0
0.23.0 (23rd May, 2022)
Changed
- Drop support for Python 3.6. (#2097)
- Use
utf-8
as the default character set, instead of falling back tocharset-normalizer
for auto-detection. To enable automatic character set detection, see the documentation. (#2165)Fixed
- Fix
URL.copy_with
for some oddly formed URL cases. (#2185)- Digest authentication should use case-insensitive comparison for determining which algorithm is being used. (#2204)
- Fix console markup escaping in command line client. (#1866)
- When files are used in multipart upload, ensure we always seek to the start of the file. (#2065)
- Ensure that
iter_bytes
never yields zero-length chunks. (#2068)- Preserve
Authorization
header for redirects that are to the same origin, but are anhttp
-to-https
upgrade. (#2074)- When responses have binary output, don't print the output to the console in the command line client. Use output like
<16086 bytes of binary data>
instead. (#2076)- Fix display of
--proxies
argument in the command line client help. (#2125)- Close responses when task cancellations occur during stream reading. (#2156)
- Fix type error on accessing
.request
onHTTPError
exceptions. (#2158)Version 0.22.0
0.22.0 (26th January, 2022)
Added
- Support for the SOCKS5 proxy protocol via the
socksio
package. (#2034)- Support for custom headers in multipart/form-data requests (#1936)
Fixed
- Don't perform unreliable close/warning on
__del__
with unclosed clients. (#2026)- Fix
Headers.update(...)
to correctly handle repeated headers (#2038)Version 0.21.3
0.21.3 (6th January, 2022)
Fixed
- Fix streaming uploads using
SyncByteStream
orAsyncByteStream
. Regression in 0.21.2. (#2016)Version 0.21.2
0.21.2 (5th January, 2022)
Fixed
Version 0.21.1
... (truncated)
Sourced from httpx's changelog.
0.23.0 (23rd May, 2022)
Changed
- Drop support for Python 3.6. (#2097)
- Use
utf-8
as the default character set, instead of falling back tocharset-normalizer
for auto-detection. To enable automatic character set detection, see the documentation. (#2165)Fixed
- Fix
URL.copy_with
for some oddly formed URL cases. (#2185)- Digest authentication should use case-insensitive comparison for determining which algorithm is being used. (#2204)
- Fix console markup escaping in command line client. (#1866)
- When files are used in multipart upload, ensure we always seek to the start of the file. (#2065)
- Ensure that
iter_bytes
never yields zero-length chunks. (#2068)- Preserve
Authorization
header for redirects that are to the same origin, but are anhttp
-to-https
upgrade. (#2074)- When responses have binary output, don't print the output to the console in the command line client. Use output like
<16086 bytes of binary data>
instead. (#2076)- Fix display of
--proxies
argument in the command line client help. (#2125)- Close responses when task cancellations occur during stream reading. (#2156)
- Fix type error on accessing
.request
onHTTPError
exceptions. (#2158)0.22.0 (26th January, 2022)
Added
- Support for the SOCKS5 proxy protocol via the
socksio
package. (#2034)- Support for custom headers in multipart/form-data requests (#1936)
Fixed
- Don't perform unreliable close/warning on
__del__
with unclosed clients. (#2026)- Fix
Headers.update(...)
to correctly handle repeated headers (#2038)0.21.3 (6th January, 2022)
Fixed
- Fix streaming uploads using
SyncByteStream
orAsyncByteStream
. Regression in 0.21.2. (#2016)0.21.2 (5th January, 2022)
Fixed
0.21.1 (16th November, 2021)
Fixed
- The
response.url
property is now correctly annotated asURL
, instead ofOptional[URL]
. (#1940)
... (truncated)
89cdd90
Version 0.23.0 (#2214)1c33a28
Make charset auto-detection optional. (#2165)940d61b
Removed curio from async.md (#2240)14a1704
Switch to explicit typing.Optional
throughout (#2096)9673a35
Drop async_generator requirement (#2228)5eba32a
Remove RequestBodyUnavailable
from module docstring (#2226)6f31bc4
Bump mkdocs-material from 8.1.4 to 8.2.14 (#2218)c5eb4b8
Bump cryptography from 36.0.2 to 37.0.2 (#2217)1a526cf
Bump actions/checkout from 2 to 3 (#2216)7a53543
Bump actions/setup-python from 1 to 3 (#2215)Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
Bumps pyjwt from 1.7.1 to 2.4.0.
Sourced from pyjwt's releases.
2.4.0
Security
- [CVE-2022-29217] Prevent key confusion through non-blocklisted public key formats. https://github.com/jpadilla/pyjwt/security/advisories/GHSA-ffqj-6fqr-9h24
What's Changed
- Add support for Python 3.10 by
@hugovk
in jpadilla/pyjwt#699- Don't use implicit optionals by
@rekyungmin
in jpadilla/pyjwt#705- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#708- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#710- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#711- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#712- documentation fix: show correct scope for decode_complete() by
@sseering
in jpadilla/pyjwt#661- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#716- Explicit check the key for ECAlgorithm by
@estin
in jpadilla/pyjwt#713- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#720- api_jwk: Add PyJWKSet.getitem by
@woodruffw
in jpadilla/pyjwt#725- Update usage.rst by
@guneybilen
in jpadilla/pyjwt#727- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#728- fix: Update copyright information by
@kkirsche
in jpadilla/pyjwt#729- Docs: mention performance reasons for reusing RSAPrivateKey when encoding by
@dmahr1
in jpadilla/pyjwt#734- Fixed typo in usage.rst by
@israelabraham
in jpadilla/pyjwt#738- Add detached payload support for JWS encoding and decoding by
@fviard
in jpadilla/pyjwt#723- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#740- Raise DeprecationWarning for jwt.decode(verify=...) by
@akx
in jpadilla/pyjwt#742- Don't mutate options dictionary in .decode_complete() by
@akx
in jpadilla/pyjwt#743- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#748- Replace various string interpolations with f-strings by
@akx
in jpadilla/pyjwt#744- Update CHANGELOG.rst by
@hipertracker
in jpadilla/pyjwt#751New Contributors
@hugovk
made their first contribution in jpadilla/pyjwt#699@rekyungmin
made their first contribution in jpadilla/pyjwt#705@sseering
made their first contribution in jpadilla/pyjwt#661@estin
made their first contribution in jpadilla/pyjwt#713@woodruffw
made their first contribution in jpadilla/pyjwt#725@guneybilen
made their first contribution in jpadilla/pyjwt#727@dmahr1
made their first contribution in jpadilla/pyjwt#734@israelabraham
made their first contribution in jpadilla/pyjwt#738@fviard
made their first contribution in jpadilla/pyjwt#723@akx
made their first contribution in jpadilla/pyjwt#742@hipertracker
made their first contribution in jpadilla/pyjwt#751Full Changelog: https://github.com/jpadilla/pyjwt/compare/2.3.0...2.4.0
2.3.0
What's Changed
- [pre-commit.ci] pre-commit autoupdate by
@pre-commit-ci
in jpadilla/pyjwt#700- Add exception chaining by
@ehdgua01
in jpadilla/pyjwt#702- Revert "Remove arbitrary kwargs." by
@auvipy
in jpadilla/pyjwt#701
... (truncated)
Sourced from pyjwt's changelog.
v2.4.0 <https://github.com/jpadilla/pyjwt/compare/2.3.0...2.4.0>
__Security
- [CVE-2022-29217] Prevent key confusion through non-blocklisted public key formats. https://github.com/jpadilla/pyjwt/security/advisories/GHSA-ffqj-6fqr-9h24
Changed
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---- Explicit check the key for ECAlgorithm by @estin in https://github.com/jpadilla/pyjwt/pull/713 - Raise DeprecationWarning for jwt.decode(verify=...) by @akx in https://github.com/jpadilla/pyjwt/pull/742 Fixed ~~~~~ - Don't use implicit optionals by @rekyungmin in https://github.com/jpadilla/pyjwt/pull/705 - documentation fix: show correct scope for decode_complete() by @sseering in https://github.com/jpadilla/pyjwt/pull/661 - fix: Update copyright information by @kkirsche in https://github.com/jpadilla/pyjwt/pull/729 - Don't mutate options dictionary in .decode_complete() by @akx in https://github.com/jpadilla/pyjwt/pull/743 Added ~~~~~ - Add support for Python 3.10 by @hugovk in https://github.com/jpadilla/pyjwt/pull/699 - api_jwk: Add PyJWKSet.__getitem__ by @woodruffw in https://github.com/jpadilla/pyjwt/pull/725 - Update usage.rst by @guneybilen in https://github.com/jpadilla/pyjwt/pull/727 - Docs: mention performance reasons for reusing RSAPrivateKey when encoding by @dmahr1 in https://github.com/jpadilla/pyjwt/pull/734 - Fixed typo in usage.rst by @israelabraham in https://github.com/jpadilla/pyjwt/pull/738 - Add detached payload support for JWS encoding and decoding by @fviard in https://github.com/jpadilla/pyjwt/pull/723 - Replace various string interpolations with f-strings by @akx in https://github.com/jpadilla/pyjwt/pull/744 - Update CHANGELOG.rst by @hipertracker in https://github.com/jpadilla/pyjwt/pull/751 `v2.3.0 <https://github.com/jpadilla/pyjwt/compare/2.2.0...2.3.0>`__ ----------------------------------------------------------------------- Fixed ~~~~~ - Revert "Remove arbitrary kwargs." `[#701](https://github.com/jpadilla/pyjwt/issues/701) <https://github.com/jpadilla/pyjwt/pull/701>`__ Added ~~~~~ - Add exception chaining `[#702](https://github.com/jpadilla/pyjwt/issues/702) <https://github.com/jpadilla/pyjwt/pull/702>`__ `v2.2.0 <https://github.com/jpadilla/pyjwt/compare/2.1.0...2.2.0>`__ ----------------------------------------------------------------------- </tr></table> </code></pre> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/jpadilla/pyjwt/commit/83ff831a4d11190e3a0bed781da43f8d84352653"><code>83ff831</code></a> chore: update changelog</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/4c1ce8fd9019dd312ff257b5141cdb6d897379d9"><code>4c1ce8f</code></a> chore: update changelog</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/96f3f0275745c5a455c019a0d3476a054980e8ea"><code>96f3f02</code></a> fix: failing advisory test</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc"><code>9c52867</code></a> Merge pull request from GHSA-ffqj-6fqr-9h24</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/24b29adfebcb4f057a3cef5aaf35653bc0c1c8cc"><code>24b29ad</code></a> Update CHANGELOG.rst (<a href="https://github-redirect.dependabot.com/jpadilla/pyjwt/issues/751">#751</a>)</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/31f5acb8fb3ec6cdfe2b1b0a4a8f329b5f3ca67f"><code>31f5acb</code></a> Replace various string interpolations with f-strings (<a href="https://github-redirect.dependabot.com/jpadilla/pyjwt/issues/744">#744</a>)</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/5581a31c21de70444c1162bcfa29f7e0fc86edda"><code>5581a31</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://github-redirect.dependabot.com/jpadilla/pyjwt/issues/748">#748</a>)</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/3d4d82248f1120c87f1f4e0e8793eaa1d54843a6"><code>3d4d822</code></a> Don't mutate options dictionary in .decode_complete() (<a href="https://github-redirect.dependabot.com/jpadilla/pyjwt/issues/743">#743</a>)</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/1f1fe15bb41846c602b3e106176b2c692b93a613"><code>1f1fe15</code></a> Add a deprecation warning when jwt.decode() is called with the legacy verify=...</li> <li><a href="https://github.com/jpadilla/pyjwt/commit/35fa28e59d99b99c6a780d2a029a74d6bbba8b1e"><code>35fa28e</code></a> [pre-commit.ci] pre-commit autoupdate (<a href="https://github-redirect.dependabot.com/jpadilla/pyjwt/issues/740">#740</a>)</li> <li>Additional commits viewable in <a href="https://github.com/jpadilla/pyjwt/compare/1.7.1...2.4.0">compare view</a></li> </ul> </details> <br />
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/Buuntu/fastapi-react/network/alerts).Releases
v1.2.0 2021-02-02 23:57:12
Updates FastAPI and remove Starlette as dependency
v1.1.0 2020-07-27 18:44:53
Replace snapshot tests with react-testing-library
Software engineer interested in bioinformatics, automation, serverless
GitHub Repositoryfastapi postgres cookiecutter react react-admin nginx boilerplate full-stack typescript sqlalchemy jwt oauth2 docker