GithubHelp home page GithubHelp logo

nsidnev / fastapi-realworld-example-app Goto Github PK

View Code? Open in Web Editor NEW
2.7K 42.0 622.0 1 MB

Backend logic implementation for https://github.com/gothinkster/realworld with awesome FastAPI

License: MIT License

Python 98.35% Mako 0.38% Shell 0.90% Dockerfile 0.37%
python python3 async fastapi postgresql docker docker-compose tutorial github-actions

fastapi-realworld-example-app's Introduction

image

image

image

image

image

image

image

image


NOTE: This repository is not actively maintained because this example is quite complete and does its primary goal - passing Conduit testsuite.

More modern and relevant examples can be found in other repositories with fastapi tag on GitHub.

Quickstart

First, run PostgreSQL, set environment variables and create database. For example using docker: :

export POSTGRES_DB=rwdb POSTGRES_PORT=5432 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres
docker run --name pgdb --rm -e POSTGRES_USER="$POSTGRES_USER" -e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" -e POSTGRES_DB="$POSTGRES_DB" postgres
export POSTGRES_HOST=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' pgdb)
createdb --host=$POSTGRES_HOST --port=$POSTGRES_PORT --username=$POSTGRES_USER $POSTGRES_DB

Then run the following commands to bootstrap your environment with poetry: :

git clone https://github.com/nsidnev/fastapi-realworld-example-app
cd fastapi-realworld-example-app
poetry install
poetry shell

Then create .env file (or rename and modify .env.example) in project root and set environment variables for application: :

touch .env
echo APP_ENV=dev >> .env
echo DATABASE_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB >> .env
echo SECRET_KEY=$(openssl rand -hex 32) >> .env

To run the web application in debug use:

alembic upgrade head
uvicorn app.main:app --reload

If you run into the following error in your docker container:

sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

Ensure the DATABASE_URL variable is set correctly in the .env file. It is most likely caused by POSTGRES_HOST not pointing to its localhost.

DATABASE_URL=postgresql://postgres:[email protected]:5432/rwdb

Run tests

Tests for this project are defined in the tests/ folder.

Set up environment variable DATABASE_URL or set up database_url in app/core/settings/test.py

This project uses pytest to define tests because it allows you to use the assert keyword with good formatting for failed assertations.

To run all the tests of a project, simply run the pytest command: :

$ pytest
================================================= test session starts ==================================================
platform linux -- Python 3.8.3, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: /home/some-user/user-projects/fastapi-realworld-example-app, inifile: setup.cfg, testpaths: tests
plugins: env-0.6.2, cov-2.9.0, asyncio-0.12.0
collected 90 items

tests/test_api/test_errors/test_422_error.py .                                                                   [  1%]
tests/test_api/test_errors/test_error.py .                                                                       [  2%]
tests/test_api/test_routes/test_articles.py .................................                                    [ 38%]
tests/test_api/test_routes/test_authentication.py ..                                                             [ 41%]
tests/test_api/test_routes/test_comments.py ....                                                                 [ 45%]
tests/test_api/test_routes/test_login.py ...                                                                     [ 48%]
tests/test_api/test_routes/test_profiles.py ............                                                         [ 62%]
tests/test_api/test_routes/test_registration.py ...                                                              [ 65%]
tests/test_api/test_routes/test_tags.py ..                                                                       [ 67%]
tests/test_api/test_routes/test_users.py ....................                                                    [ 90%]
tests/test_db/test_queries/test_tables.py ...                                                                    [ 93%]
tests/test_schemas/test_rw_model.py .                                                                            [ 94%]
tests/test_services/test_jwt.py .....                                                                            [100%]

============================================ 90 passed in 70.50s (0:01:10) =============================================
$

If you want to run a specific test, you can do this with this pytest feature: :

$ pytest tests/test_api/test_routes/test_users.py::test_user_can_not_take_already_used_credentials

Deployment with Docker

You must have docker and docker-compose tools installed to work with material in this section. First, create .env file like in Quickstart section or modify .env.example. POSTGRES_HOST must be specified as db or modified in docker-compose.yml also. Then just run:

docker-compose up -d db
docker-compose up -d app

Application will be available on localhost in your browser.

Web routes

All routes are available on /docs or /redoc paths with Swagger or ReDoc.

Project structure

Files related to application are in the app or tests directories. Application parts are:

app
├── api              - web related stuff.
│   ├── dependencies - dependencies for routes definition.
│   ├── errors       - definition of error handlers.
│   └── routes       - web routes.
├── core             - application configuration, startup events, logging.
├── db               - db related stuff.
│   ├── migrations   - manually written alembic migrations.
│   └── repositories - all crud stuff.
├── models           - pydantic models for this application.
│   ├── domain       - main models that are used almost everywhere.
│   └── schemas      - schemas for using in web routes.
├── resources        - strings that are used in web responses.
├── services         - logic that is not just crud related.
└── main.py          - FastAPI application creation and configuration.

fastapi-realworld-example-app's People

Contributors

andrewguest avatar dependabot-preview[bot] avatar dependabot[bot] avatar guylyu avatar nsidnev avatar peterdeme avatar rlam3 avatar samuelfirst avatar sevdimali avatar thuvh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastapi-realworld-example-app's Issues

error views

I'm total beginner with fastapi and i have a lot of problem.
The uvicorn server start ok and coonection with db is okey but at localhost(or 127.0.0.1) or at localhost:8000
i have :
{"errors":["Not Found"]}.

Instead with docker-compose i have
"file not found"
on docker-logs app

Any help it's appreciate. Thanks

how we can apply transaction to this project?

For example in one API.
I am calling a lot of methods from repositories and updating a lot of tables. So if there will be exceptions how can I roll back changes?

by the way thanks for such a great project!!! 🥇

User registration failed "relation "users" does not exist"

Dear nsidnev,

I'm a fan of your architecture in this application. Unfortunately, I cannot figure out the error from the registration side:

"asyncpg.exceptions.UndefinedTableError: relation "users" does not exist"

I attached an image on how it looks like. I hope we could figure this out.
Screenshot 2022-04-16 at 13 20 44

Screenshot 2022-04-16 at 13 21 56

Cheers and stay healthy.

Contributing

HI!

I found ways to fix the typing in several places where now is "type: ignore". Can I suggest my PR that solves this? How can I do this?

Possible Deadlock on Connection Pool

This is less an issue and more a gotcha that I ran into when load testing, but I thought it would be worth noting since it took me a good bit of time to figure out, and I'm curious what people's thoughts on this might be.

If you're acquiring pool connections in Depends arguments, you can run into a connection leak that deadlocks your application if you exhaust the pool. This is a result of how dependencies are fulfilled in FastAPI, but following a pattern used in this example app can cause the behavior:

The get_repository dependency (here) returns an async generator, which gets you a Repository and handles the acquiring and release of the associated connection via the async with deal. The problem arises if you have multiple dependencies that acquire connections from the pool and you exhaust the pool. Because the dependencies are executed in an AsyncExitStack, connections are released into the pool in the reverse order that they acquired.

For instance, the update_current_user route

async def update_current_user(
    user_update: UserInUpdate = Body(..., embed=True, alias="user"),
    current_user: User = Depends(get_current_user_authorizer()),
    users_repo: UsersRepository = Depends(get_repository(UsersRepository)),
)

depends on get_repository(UsersRepository) and get_current_user_authorizer, which transitively depends on a get_repository(UsersRepository) as well. When a request comes in, ideally you'll

  1. acquire connection 1 in order to create the UsersRepository needed for get_current_user_authorizer
  2. acquire connection 2 to create the second UsersRepository needed for users_repo
  3. execute the method, hooray
  4. release connection 2
  5. release connection 1

But if you have a max_size of 1 on your connection pool, you'll

  1. acquire connection 1
  2. wait forever to acquire a connection because the release of connection 1 depends on the acquiring and release of this hypothetical connection 2, which depends on connection 1 being released

You'd probably never be so reckless as to have such a measly connection pool as that, but if you're under high load, what would have been brief pool exhaustion causing slight delays in acquiring connections quickly escalates into deadlock forever. Even if you add timeouts for acquiring connections and for query execution, you still suffer a pretty horrible performance hit this way, getting lots of timeout errors you wouldn't be getting otherwise, and holding connections open for a lot longer than you need to. It seems to me like the easiest way around this would be to have the Repository classes be long lived and take a Pool init arg rather than Connection so that they can handle the connection lifecycles themselves, but I'm curious if there are better ways, since FastAPI and a lot of this async business are pretty new to me.

I should note that this repo has been a tremendous resource, and I'm very grateful for its existence.

There seems to be a bug?

QQ截图20200729140346

It seems to be user_in_db = await queries.get_user_by_username(username=username)

I'm just guessing, because I'm not very good at it.

If not, forgive me for interrupting

it in app.db.repositories.users, 55 line

Set proper host in DB_CONNECTION in .env.example

Now:
DB_CONNECTION=postgres://postgres:postgres@localhost/postgres

Expected:
DB_CONNECTION=postgres://postgres:postgres@db/postgres

The hostname for connection is the same as a service name, so this information is considered permanent.

500 error getting articles

Testing with zap-api-scan.py I have discovered various 500 status codes in the API. For example:
curl -X GET "http://localhost:8000/api/articles?tag=%00&author=author&favorited=favorited&limit=20&offset=0" -H "accept: application/json"
The complete report look like this:

ZAP Scanning Report

Generated on Thu, 25 Feb 2021 15:54:01

Summary of Alerts

Risk Level Number of Alerts
High 0
Medium 0
Low 3
Informational 1

Alerts

Name Risk Level Number of Instances
A Server Error response code was returned by the server Low 3
Unexpected Content-Type was returned Low 3
X-Content-Type-Options Header Missing Low 3
A Client Error response code was returned by the server Informational 435

Alert Detail

A Server Error response code was returned by the server

Low (High)

Description

A response code of 500 was returned by the server.

This may indicate that the application is failing to handle unexpected input correctly.

Raised by the 'Alert on HTTP Response Code Error' script

Instances: 3

Solution

Reference

CWE Id : 388

WASC Id : 20

Source ID : 4

Unexpected Content-Type was returned

Low (High)

Description

A Content-Type of text/plain was returned by the server.

This is not one of the types expected to be returned by an API.

Raised by the 'Alert on Unexpected Content Types' script

Instances: 3

Solution

Reference

Source ID : 4

X-Content-Type-Options Header Missing

Low (Medium)

Description

The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.

Instances: 3

Solution

Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages.

If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.

Other information

This issue still applies to error type pages (401, 403, 500, etc.) as those pages are often still affected by injection issues, in which case there is still concern for browsers sniffing pages away from their actual content type.

At "High" threshold this scan rule will not alert on client or server error responses.

Reference

CWE Id : 16

WASC Id : 15

Source ID : 3

A Client Error response code was returned by the server

Informational (High)

Description

A response code of 404 was returned by the server.

This may indicate that the application is failing to handle unexpected input correctly.

Raised by the 'Alert on HTTP Response Code Error' script

Instances: 435

Solution

Reference

CWE Id : 388

WASC Id : 20

Source ID : 4

queries.pyi / aiosql

I'm attempting to create a new table entity, and writing a repository to query the database.

  1. I was trying to figure out how to update env.py to support auto-generation with alembic, but wasn't sure where the "base metadata" lives, or how to specify it exactly. Do I need a file that imports all the models in app/models/domain?
  2. queries.py references queries.pyi. The readme doesn't specify how to use aiosql, so I'm not sure what to do with it exactly. Is it required for doing queries?

Update test running guide

I think this need some guideline to how to run unit test under the test dir for some beginners
It will be very helpful, so much thanks

scalability issue

fastapi being fast and scalable, but it seems this example won't handle readworld benchmark. To repro, run this and benchmark with wrk -t1 -c100 -d10s --latency http://localhost:8000/api/articles, and any api accessing db is stuck :(

Maybe we could increase the default MAX_CONNECTIONS_COUNT to a higher value.

Alembic configuration

Is alembic configured correctly? When I change the model, and run alembic revision, it generates an empty migration. It does not seem to pick up the changes made in models files.

How can I run this app on Google app engine without requirements.txt

Hi there.

I start using fastAPI before a few weeks and I love it!
And I read a lot about poetry, and I get the idea, but I still not managed to deploy this app to Google app engine engine, because the requirements file is missing.

I try to digging into it, but I fill that I missed something, especially in gae, but also in other services and tools that require requirements.txt

Thanks for any help!

No module named dotenv

After install environment with poetry and setup dot env file, i run command alembic upgrade head and receive the follow error

Traceback (most recent call last):
  File "pydantic/env_settings.py", line 225, in pydantic.env_settings.read_env_file
ModuleNotFoundError: No module named 'dotenv'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/bin/alembic", line 8, in <module>
    sys.exit(main())
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/config.py", line 588, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/config.py", line 582, in main
    self.run_cmd(cfg, options)
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/config.py", line 559, in run_cmd
    fn(
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/command.py", line 320, in upgrade
    script.run_env()
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/script/base.py", line 563, in run_env
    util.load_python_file(self.dir, "env.py")
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/util/pyfiles.py", line 92, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/thuvh/.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-2RFB7GKt-py3.8/lib/python3.8/site-packages/alembic/util/pyfiles.py", line 108, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "./app/db/migrations/env.py", line 12, in <module>
    SETTINGS = get_app_settings()
  File "/home/thuvh/workspaces/aopt/app/core/config.py", line 19, in get_app_settings
    app_env = BaseAppSettings().app_env
  File "pydantic/env_settings.py", line 37, in pydantic.env_settings.BaseSettings.__init__
  File "pydantic/env_settings.py", line 63, in pydantic.env_settings.BaseSettings._build_values
  File "pydantic/env_settings.py", line 154, in pydantic.env_settings.EnvSettingsSource.__call__
  File "pydantic/env_settings.py", line 227, in pydantic.env_settings.read_env_file
ImportError: python-dotenv is not installed, run `pip install pydantic[dotenv]`

Add documentation

It will be great to have a detailed documentation that covers directory structure, app functionalities. This will really help new users to be able to grasp your work easily.

Test will try to use docker, even when told not to....

I created a local database that can be approached with ident authentication.

Then in .env specified:
DB_CONNECTION=postgresql+psycopg2:///real_world_application

In an ipython shell I verified that I can use this string to the database.

Still $ USE_LOCAL_DB_FOR_TEST=True pytest results in:


During handling of the above exception, another exception occurred:
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/docker/api/client.py:214: in _retrieve_server_version
    return self.version(api_version=False)["ApiVersion"]
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/docker/api/daemon.py:181: in version
    return self._result(self._get(url), json=True)
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/docker/utils/decorators.py:46: in inner
    return f(self, *args, **kwargs)
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/docker/api/client.py:237: in _get
    return self.get(url, **self._set_request_timeout(kwargs))
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/requests/sessions.py:555: in get
    return self.request('GET', url, **kwargs)
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/requests/sessions.py:542: in request
    resp = self.send(prep, **send_kwargs)
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/requests/sessions.py:655: in send
    r = adapter.send(request, **kwargs)
../../../.cache/pypoetry/virtualenvs/fastapi-realworld-example-app-qr8Xy3u9-py3.8/lib/python3.8/site-packages/requests/adapters.py:498: in send
    raise ConnectionError(err, request=request)
E   requests.exceptions.ConnectionError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

(Lots of the above...)

Unsupported authorization type

Hi,

Thank you for creating this project.

Can you please let me know how I can authenticate in the Swagger UI?

Here's what I am doing right now:

  1. POST username and email to /api/users/login which returns a TOKEN.
  2. I click on the authorize button in the UI and enter Token TOKEN.

But, when I try out any of the endpoints, it gives me unsupported authorization type error.

Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.