DEV Community

Cover image for I made full-stack templates with 4 backend frameworks
Justin Joco
Justin Joco

Posted on

I made full-stack templates with 4 backend frameworks

Context

As someone who likes doing multiple projects, I find it tedious to keep starting from scratch every time I wanna make something new. So, I created several full-stack templates in Rust, Go, and Python, so that I can hit the ground running when I begin a new full stack project.

High Level Design

All four templates are essentially the same application: an application that allows users to read, add, update, and delete books into some persistent storage. The architecture is identical as well: a React frontend on top of a backend API service, which stores its data in a Postgres DB with a Redis cache in front of the DB. The DB is initialized with some seed data of a Book entity. The Redis caches are set up with the same DB data, with keys formatted like "book:" mapped to a JSON string. Each Dockerfile sets up the dependencies of the respective framework, including Postgres and Redis integration libraries, CORS middleware, and DB migrations.

The overall architectures were intentionally identical since my goal for these templates was to vary the backend framework, but keep everything else the same. Thus, what did not change in each template were the frontend, the book DB model, the DB engine, and the cache. To keep things simple, I decided not to create multiple tables with relations between each other.

Frameworks Chosen

Specifically, the backend frameworks I chose were Flask (Python), Django REST Framework (Python), Gin (Golang), and Actix Web (Rust). Sure, I could have chosen Java Spring Boot or Express.js, but I already have professional experiences with those, and I wanted to learn something out of my current comfort zone. I attempted to write each backend API service to follow the Controller-Service-Repository pattern, in which the Controller handles API requests/responses, the Service handles business logic, and the Repository handles DB/Redis integrations. I mostly did this to compare and contrast how flexible and opinionated each framework was.

Here's why I chose those four specifically:

  • Flask and DRF because these are popular Python web frameworks
  • Gin because I had some experience in Go, and Go is still a growing language
  • Actix Web because I wanted to learn Rust due to its high performance and memory safety

Here are the links to my templates, for those curious:

Implementations

I started off with Flask first since I already did a project on it before. I did a whole bunch of copying of my previous project and removed a good amount of it to simplify the service implementation. When I did my previous project, there was a good amount of things to install, including SQLAlchemy and a good amount of libraries to convert API request bodies into DB models and vice versa for API responses. Fortunately, it was pretty straightforward to follow the Controller-Service-Repository pattern.

Then, I implemented the backend in DRF. Fortunately, since this framework already came with a lot of features, I didn't have to install that many extra libraries. For example, it already had a Postgres integration as part of its framework. When I attempted to implement the CSR pattern, it was very apparent that DRF is very opinionated, and it was much simpler to follow DRF's uses of Serializers, ModelSerializers, and Views than to adhere to CSR.

Next, I did Go-Gin since the last time I used it was around 2021, and I was fairly rusty. By far, this was the fastest to implement, as it really took me only a day or two to implement with the CSR pattern. It was great that I didn't need to implement any serializers/deserializers of requests/responses and DB models since Go and Gin pretty much handled all that out of the box. Like with Flask, I had to download a good amount of libraries for DB and Redis integration. Honestly, it was way easier to set up DB migrations and seeding here than in either of the Python frameworks since there was a dedicated entrypoint in the main.go file, and I could order the initialization of everything I needed there instead of having the framework automatically try to do it for me.

Finally, I wrote the Rust-Actix Web backend because I knew that this would have the most complicated implementation, and I was correct. Due to Rust's focus on memory and type safety (and since I'm new to the language), this easily took me the longest to implement since I fought with the compiler a lot. It got to the point where I almost gave up. One thing I found interesting is that third-party crates have feature flags for extra features, which was something I didn't have to deal with in any of the previous services. Fortunately, I was able to figure it out after I switched the Postgres library from diesel to sqlx and understanding how dealing with local modules and crates worked. There were a good amount of third-party libraries I needed to download, including one analogous to the SQL Decimal type. Implementing the CSR pattern was already pretty straightforward, though my "controller" was file of functions since the actix-web endpoint handlers work best with functions. One thing I also noticed was the annoying long times it took to compile and for cargo to check my code.

Screenshots

Initial Get Books table
Image description

Retrieve book with id = "75d78c06-f134-4d9c-b1ae-c3e28d312faa"
Image description

Insert a new book component
Image description

Get books table with newly inserted book

Image description

Partial update of book with id = "75d78c06-f134-4d9c-b1ae-c3e28d312faa"
Image description

Get Books table with book with id = "75d78c06-f134-4d9c-b1ae-c3e28d312faa" updated

Image description

Delete book with id = "027be1fa-eaaf-4a25-aa45-dc37a7fd9079"
Image description

Get books table with book with id = "027be1fa-eaaf-4a25-aa45-dc37a7fd9079" deleted
Image description

Conclusion

Creating simple CRUD APIs using these four frameworks was fun to do. Based on my experiences, if I want to make an infrastructure project like an event logger, a rate limiter, or anything that requires high performance, I would use write it in either Go or Rust. If my project is more application-focused like a url shortener, or Open Table, the bells and whistles of Flask or DRF would be okay to implement them in.

If I were to rank on much I enjoyed implementing the same backend API service from lowest to highest, it would be the following:

  • Go
  • Flask
  • Django REST Framework
  • Rust

This coincidentally is the same order on how difficult it was to implement, from easiest to hardest. Since Go and Flask were the easiest to work with, I would use those if I were to change my datasource from Postgres to some NoSQL DB like Cassandra or neo4j.

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay