The Basics
Installation
To install FastAPI Listing, run:
pip install fastapi-listing
The Dao (Data Access Object) layer
FastAPI Listing uses a dao layer.
Benefits
A dedicated place for writing queries
Better Separation
Ability to change queries independently
Provides common code usage for more than one place
imports look cleaner
Metaphorically “a dedicated place where you cultivate your ingredients for cooking purpose” (stolen from sqlalchemy docs)
FastAPI Listing uses single table Dao. Each dao class will be bound with single orm model class 📝.
Dao objects
- class GenericDao
when creating dao class extend GenericDao which comes with necessary setup code.
Each Dao object support two protected (limiting their scope to dao layer only) session attributes.
_read_db and _write_db
You can use these attributes to communicate with the database. Provides early preparation of when you might need to implement master slave architecture. Non master slave arch users can point both of these attributes to same db as well. It’s simple.
Dao class attributes
- GenericDao.model
The sqlalchemy model class. Attribute type - Required
- GenericDao.name
User defined name of dao class, should be unique. Attribute type - Required
The Strategy layer
Encapsulates process of: * writing data fetch logics (Query Strategy) * applying sorting(if any) after fetching data (Sorting Strategy) * paginating data at the end (Paginating Strattegy)
Inspiration of using strategy pattern for this: Depending upon logged in user/applied query filter/performance requirement/legacy based database schema(poorly managed)/data visual limiting due to maybe role of user. You will write multiple ways to prepare queries to fetch data, or different technique to handle sorting, or a lazy paginator etc. In any case this is a really good way to handle multiple logic implementations and their compositions.
Query Strategy
Logical layer to decide on a listing query in a context. By default comes with a default_query strategy which generates a
select a,b,c,d from some_table query using sqlalchemy where a,b,c,d are columns given by the user.
For simple use cases this gets the work done.
- class QueryStrategy
You can easily create your custom Query Strategy by extending base class.
➡️ Taking a real world example where using strategy pattern can be helpful:
You have an employee table and hierarchy Director*->Assistant Director*->Division Managers*->Managers*->Leads*->teams.
You need to design an API to show list of employees associated to logged-in user only. For the sake of this example lets focus on query part only.
With strategy you have two ways of achieving this.
➡️ Creating context related query strategies:
class DirectorQueryForEmp(QueryStrategy)
class AssistantDirectorQueryForEmp(QueryStrategy)
class DivisionManagerQueryForEmp(QueryStrategy)
class ManagersQueryForEmp(QueryStrategy)
class LeadsQueryForEmp(QueryStrategy)
You can abstract and encapsulate relevant logic to make a decision on logged in user basis. You can choose which one to call at runtime.
Or
➡️ Encapsulate the whole thing into one:
class EmployeeQuery(QueryStrategy)
And implement context based logics in one place. Choosing to write in any of above style is a personal decision based on project requirements.
Benefit of above approach:
Context is clear by just a look
light weight containers of logical instructions
Decoupled and easy to extend
Much Easier to incorporate new relevant features like adding for new role or super user.
Sorting Strategy
Responsible for applying sorting scheme(sql native sorting) on your query. Simple as it sound, nothing fancy here.
- class SortingOrderStrategy
SortingOrderStrategy class knows two client site keywords asc or dsc and applies sorting scheme on basis of this 📝.
🤯You are using different keywords to make sorting decision? No worries 😉 Make FastAPI Listing adapt to it.
Paginator Strategy
Simple Paginator to paginate your database queries and return paginated response to your clients.
- class PaginationStrategy
Easily define pagination params.
Support dynamic page resizing.
You can configure
default_page_sizeto return default number of items if client made a request without pagination paramsYou can configure
max_page_size, to avoid memory choke on absurd page size demands from clients.Easily implement your own custom paginator to add more features like lazy loading or range based slicing.
🤯You have an existing set of pagination params. can you still use it? Yes! 😉 Make FastAPI Listing adapt to it.
The Filters layer
The most used feature of any listing service easily, and maintaining filters is an art in itself ❤️.
Abstracts away the complex procedure of applying filters, No more branching (if else) in your listing API even if you have more than a dozen filters, with this you can write performance packed robust filters.
Inspired by django-admin design of writing and maintaining filters. Create filter anywhere easy to import ❤️ like any independent
facade API. You will see how inbuilt generic_filters will make it easy and super fast to integrate filters in your listing APIs.
🤯 Can it support your existing clients filter parameters? Ofcourse! 😉 Make FastAPI Listing adapt to it.
The Interceptor layer
Allows users to write custom execution plan for filters/Sorters.
Default filter execution plan follows iterative approach when one or more filters are applied by clients.
Default sorter execution plan allows sort on one param at a time.
Reason of existence❓️ - In my personal experience there are special situations when applying two or many filters directly could cause multitude of problems if applied in one by one iterative fashion. Maybe you wanna skip one or combine two filter into one and form a more optimised and robust query for your db to avoid performance hiccups.
Or
Allow sorting on more than one field at a time (I personally don’t like the idea as for larger tables it degrades the performance) The best way in my humble opinion is to shorten your data via filters and then sort on your will.
So now you know you can intercept the way filters and sorters are applied and add your custom behaviours to it.
Params Adapter layer
Everyone implements filter/sorter/paginator layers at their client site differently. For example stackoverflow🧐:
You might have a different approach, which is perfectly fine. This is where you can use FastAPI Listing to adjust to the
parameters of your client’s site by utilizing CoreListingParamsAdapter 🤓. With this, you can access your HTTP request
object and parse the query parameters in a way that FastAPI Listing can comprehend.
FastAPI Listing uses sort, filter and pagination as keys for the adapter. The adapter should then return the
translated parameters signaled at the native level.
Now, you may wonder how FastAPI Listing natively understands the mentioned parameters:
Filter:
[{"field": "<your_field>", "value": {"search": "<your_value>"}}]- This represents a list of filters applied by clients, where multiple filters can be applied simultaneously.Sort:
[{"field": "<your_field>", "type": "<asc or dsc>"}]- This indicates a list of sorting instructions. While the default supports single sorting (as explained above), customization is possible.Pagination:
{"pageSize": "<integer pagesize>", "page": "<integer page number>"}- These are pagination parameters that support dynamic resizing of the page.
This feature proves immensely beneficial for user with existing operational services seeking an enhanced solution to manage their current codebase. By leveraging this library, user can potentially integrate it without necessitating modificatin to their remote client site code. Consequently, FastAPI Listing Service can seamlessly adapt to their requirements.
Moreover, Filters also provide varying semantics for parameters based on ranges and list.
Conclusion
That’s it folks that’s all for the theory. If you were able to come this far I believe you have a basic understanding of all the components. In the next section we will start with Tutorials.