Convert Figma logo to code with AI

nsidnev logofastapi-realworld-example-app

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

2,792
629
2,792
0

Top Related Projects

Example Spring codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld API spec.

ASP.NET Core backend implementation for RealWorld

Exemplary real world backend API built with Laravel

Quick Overview

The nsidnev/fastapi-realworld-example-app is a comprehensive example application showcasing the implementation of the RealWorld specification using FastAPI. It demonstrates best practices for building a robust backend API with FastAPI, including authentication, database integration, and RESTful endpoint design.

Pros

  • Provides a complete, production-ready FastAPI application structure
  • Implements JWT authentication and role-based access control
  • Includes database integration with SQLAlchemy and Alembic for migrations
  • Demonstrates comprehensive testing practices with pytest

Cons

  • May be overwhelming for beginners due to its complexity
  • Requires understanding of multiple libraries and concepts (FastAPI, SQLAlchemy, Alembic, etc.)
  • Documentation could be more extensive for some advanced features
  • Some dependencies may become outdated over time

Code Examples

  1. Defining a FastAPI route with dependency injection:
@router.get("", response_model=List[ArticleInResponse], name="articles:list-articles")
async def list_articles(
    tag: str = "",
    author: str = "",
    favorited: str = "",
    limit: int = Query(DEFAULT_ARTICLES_LIMIT, ge=1),
    offset: int = Query(DEFAULT_ARTICLES_OFFSET, ge=0),
    user: Optional[User] = Depends(get_current_user_authorizer(required=False)),
    articles_repo: ArticlesRepository = Depends(get_repository(ArticlesRepository)),
) -> List[ArticleInResponse]:
    articles = await articles_repo.filter_articles(
        tag=tag,
        author=author,
        favorited=favorited,
        limit=limit,
        offset=offset,
        requested_user=user,
    )
    return [ArticleInResponse(**article.dict()) for article in articles]
  1. Defining a Pydantic model for request validation:
class UserInCreate(BaseModel):
    username: str
    email: EmailStr
    password: str

    @validator("username")
    def username_alphanumeric(cls, v):
        assert v.isalnum(), "must be alphanumeric"
        return v
  1. Setting up database models with SQLAlchemy:
class Article(Base):
    __tablename__ = "articles"

    id = Column(Integer, primary_key=True)
    slug = Column(String, unique=True, nullable=False, index=True)
    title = Column(String, nullable=False)
    description = Column(String, nullable=False)
    body = Column(Text, nullable=False)
    created_at = Column(DateTime, server_default=func.now(), nullable=False)
    updated_at = Column(DateTime, server_default=func.now(), nullable=False)
    author_id = Column(Integer, ForeignKey("users.id"))

Getting Started

  1. Clone the repository:

    git clone https://github.com/nsidnev/fastapi-realworld-example-app.git
    cd fastapi-realworld-example-app
    
  2. Install dependencies:

    pip install -r requirements.txt
    
  3. Set up the database:

    alembic upgrade head
    
  4. Run the application:

    uvicorn app.main:app --reload
    
  5. Access the API documentation at http://localhost:8000/docs

Competitor Comparisons

Pros of django-realworld-example-app

  • Built on Django, a mature and feature-rich web framework with a large ecosystem
  • Includes Django REST framework for robust API development
  • Provides a more traditional MVC architecture, familiar to many developers

Cons of django-realworld-example-app

  • Slower performance compared to FastAPI due to Django's synchronous nature
  • More boilerplate code required for setup and configuration
  • Less flexible for microservices and lightweight applications

Code Comparison

django-realworld-example-app:

class ArticleViewSet(mixins.CreateModelMixin,
                     mixins.ListModelMixin,
                     mixins.RetrieveModelMixin,
                     viewsets.GenericViewSet):
    lookup_field = 'slug'
    queryset = Article.objects.select_related('author', 'author__user')
    permission_classes = (IsAuthenticatedOrReadOnly,)
    serializer_class = ArticleSerializer

fastapi-realworld-example-app:

@router.get(
    "",
    response_model=ArticlesResponse,
    name="articles:list-articles",
)
async def list_articles(
    tag: str = "",
    author: str = "",
    favorited: str = "",
    limit: int = Query(20, ge=1),
    offset: int = Query(0, ge=0),
    user: User = Depends(get_current_user_authorizer(required=False)),
) -> ArticlesResponse:
    # Function implementation

The FastAPI example demonstrates a more concise and type-hinted approach, while the Django example showcases its class-based view structure.

Pros of node-express-realworld-example-app

  • More established and widely adopted technology stack (Node.js and Express)
  • Larger community support and ecosystem for Node.js and Express
  • Potentially easier to find developers familiar with the tech stack

Cons of node-express-realworld-example-app

  • Less performant compared to FastAPI's asynchronous capabilities
  • More verbose code structure compared to FastAPI's concise syntax
  • Lacks built-in API documentation features like FastAPI's Swagger UI

Code Comparison

node-express-realworld-example-app:

router.post('/users', function(req, res, next) {
  var user = new User();
  user.username = req.body.user.username;
  user.email = req.body.user.email;
  user.setPassword(req.body.user.password);
  user.save().then(function() {
    return res.json({user: user.toAuthJSON()});
  }).catch(next);
});

fastapi-realworld-example-app:

@router.post("/users", response_model=UserInResponse, status_code=HTTP_201_CREATED)
async def register_user(
    user: UserInCreate = Body(..., embed=True), db: Database = Depends(get_database)
) -> UserInResponse:
    async with db.transaction():
        db_user = await users_repo.create_user(user=user)
        access_token = security.create_access_token_for_user(db_user)
        return UserInResponse(user=UserWithToken(**db_user.dict(), token=access_token))

The FastAPI example demonstrates more concise syntax, type hinting, and built-in request validation, while the Express example shows a more traditional callback-based approach.

Example Spring codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld API spec.

Pros of spring-boot-realworld-example-app

  • Utilizes Spring Boot, a mature and widely-adopted Java framework with extensive ecosystem support
  • Offers robust dependency injection and inversion of control out of the box
  • Provides seamless integration with various databases and ORM tools

Cons of spring-boot-realworld-example-app

  • Generally more verbose and requires more boilerplate code compared to FastAPI
  • Slower development speed due to compilation and larger codebase
  • Steeper learning curve for developers new to Java and Spring ecosystem

Code Comparison

spring-boot-realworld-example-app:

@RestController
@RequestMapping(path = "/api/articles/{slug}/comments")
class CommentsController {
    @PostMapping
    public ResponseEntity<?> createComment(@PathVariable("slug") String slug,
                                           @Valid @RequestBody NewCommentParam newCommentParam,
                                           @AuthenticationPrincipal User user) {
        return ResponseEntity.ok(commentQueryService.createComment(slug, newCommentParam, user));
    }
}

fastapi-realworld-example-app:

@router.post("/{slug}/comments", response_model=CommentInResponse, tags=["comments"])
async def create_comment_for_article(
    slug: str, comment: CommentInCreate, user: User = Depends(get_current_user_authorizer()),
) -> CommentInResponse:
    return await CommentService(user).create_comment(slug, comment)

The FastAPI example demonstrates more concise syntax and type hinting, while the Spring Boot example showcases its annotation-based approach and built-in dependency injection.

ASP.NET Core backend implementation for RealWorld

Pros of aspnetcore-realworld-example-app

  • Built on ASP.NET Core, offering robust performance and scalability
  • Utilizes Entity Framework Core for efficient database operations
  • Implements a clean architecture with separation of concerns

Cons of aspnetcore-realworld-example-app

  • Steeper learning curve for developers not familiar with .NET ecosystem
  • Less flexibility compared to FastAPI's lightweight approach
  • Potentially higher resource consumption due to the .NET runtime

Code Comparison

aspnetcore-realworld-example-app:

public async Task<ArticleEnvelope> Create(string slug, UpdateArticleCommand command)
{
    var article = await _context.Articles.FirstOrDefaultAsync(x => x.Slug == slug);
    if (article == null)
    {
        throw new RestException(HttpStatusCode.NotFound);
    }
    // ... (additional code)
}

fastapi-realworld-example-app:

@router.post("/articles", response_model=ArticleInResponse, status_code=HTTP_201_CREATED)
async def create_new_article(
    article: ArticleInCreate = Body(..., embed=True),
    user: User = Depends(get_current_user_authorizer()),
    article_repo: ArticleRepository = Depends(get_repository(ArticleRepository)),
) -> ArticleInResponse:
    slug = slugify(article.title)
    # ... (additional code)

The ASP.NET Core example uses a more traditional OOP approach with async/await patterns, while the FastAPI example leverages Python's async capabilities and dependency injection. FastAPI's code is more concise and relies on type hints for request/response models.

Exemplary real world backend API built with Laravel

Pros of Laravel Realworld Example App

  • Built with Laravel, a mature PHP framework with extensive documentation and community support
  • Includes built-in authentication and authorization features
  • Offers a more traditional MVC architecture, familiar to many web developers

Cons of Laravel Realworld Example App

  • PHP may be considered less performant compared to Python for certain tasks
  • Requires more boilerplate code compared to FastAPI's minimalist approach
  • Less suitable for microservices or lightweight API-only applications

Code Comparison

Laravel Realworld Example App:

public function index()
{
    $articles = Article::latest()->paginate(20);
    return $this->respondWithPagination($articles);
}

FastAPI Realworld Example App:

@router.get("/articles", response_model=ArticlesResponse)
async def list_articles(
    limit: int = 20,
    offset: int = 0,
    articles_repo: ArticlesRepository = Depends(get_repository(ArticlesRepository)),
) -> ArticlesResponse:
    articles = await articles_repo.get_articles(limit=limit, offset=offset)
    return ArticlesResponse(articles=articles, articles_count=len(articles))

The Laravel example uses a more traditional ORM approach with built-in pagination, while the FastAPI example leverages async/await and dependency injection for a more modern, performant API design.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

.. image:: ./.github/assets/logo.png

|

.. image:: https://github.com/nsidnev/fastapi-realworld-example-app/workflows/API%20spec/badge.svg :target: https://github.com/nsidnev/fastapi-realworld-example-app

.. image:: https://github.com/nsidnev/fastapi-realworld-example-app/workflows/Tests/badge.svg :target: https://github.com/nsidnev/fastapi-realworld-example-app

.. image:: https://github.com/nsidnev/fastapi-realworld-example-app/workflows/Styles/badge.svg :target: https://github.com/nsidnev/fastapi-realworld-example-app

.. image:: https://codecov.io/gh/nsidnev/fastapi-realworld-example-app/branch/master/graph/badge.svg :target: https://codecov.io/gh/nsidnev/fastapi-realworld-example-app

.. image:: https://img.shields.io/github/license/Naereen/StrapDown.js.svg :target: https://github.com/nsidnev/fastapi-realworld-example-app/blob/master/LICENSE

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/ambv/black

.. image:: https://img.shields.io/badge/style-wemake-000000.svg :target: https://github.com/wemake-services/wemake-python-styleguide


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:postgres@0.0.0.0: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 <https://docs.pytest.org/>_ 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 <https://docs.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests>_ 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.