fastapi-realworld-example-app
Backend logic implementation for https://github.com/gothinkster/realworld with awesome FastAPI
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
- 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]
- 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
- 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
-
Clone the repository:
git clone https://github.com/nsidnev/fastapi-realworld-example-app.git cd fastapi-realworld-example-app
-
Install dependencies:
pip install -r requirements.txt
-
Set up the database:
alembic upgrade head
-
Run the application:
uvicorn app.main:app --reload
-
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
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
.. 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.
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
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot