fastapi-listing


Namefastapi-listing JSON
Version 0.3.2 PyPI version JSON
download
home_pagehttps://github.com/danielhasan1/fastapi-listing
SummaryAdvaned Data Listing Library for FastAPI
upload_time2024-01-30 10:49:35
maintainer
docs_urlNone
authorDanish Hasan
requires_python>=3.7
license
keywords starlette fastapi pydantic sqlalchemy
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # fastapi-listing

Advanced items listing library that gives you freedom to design complex listing APIs that can be read by human.

[![.github/workflows/deploy.yml](https://github.com/danielhasan1/fastapi-listing/actions/workflows/deploy.yml/badge.svg)](https://github.com/danielhasan1/fastapi-listing/actions/workflows/deploy.yml)
[![.github/workflows/tests.yml](https://github.com/danielhasan1/fastapi-listing/actions/workflows/tests.yml/badge.svg)](https://github.com/danielhasan1/fastapi-listing/actions/workflows/tests.yml) ![PyPI - Programming Language](https://img.shields.io/pypi/pyversions/fastapi-listing.svg?color=%2334D058)
[![codecov](https://codecov.io/gh/danielhasan1/fastapi-listing/branch/dev/graph/badge.svg?token=U29ZRNAH8I)](https://codecov.io/gh/danielhasan1/fastapi-listing) [![Downloads](https://static.pepy.tech/badge/fastapi-listing)](https://pepy.tech/project/fastapi-listing)

➡️ Craft powerful Listing REST APIs designed to serve websites akin to Stack Overflow:

![](https://drive.google.com/uc?export=view&id=1sCkzxi7OirmtA9gGM0LlK9dryI1dlU4U)

Comes with:
- pre defined filters
- pre defined paginator
- pre defined sorter

## Advantage
- simplify the intricate process of designing and developing complex listing APIs
- Design components(USP) and plug them from anywhere
- Components can be **reusable**
- Best for fast changing needs

## Usage

➡️ With the New Compact Version(Older version was provided with a guide style which is supported by this version as well)
create your listing API in 2 easy steps 🥳

1️⃣ Create a Dao (Data Access Object) layer (a simple class)

```python
from fastapi_listing.dao import GenericDao
from app.model import Employee


# your dao (data access object) placed here for the sake of example
class EmployeeDao(GenericDao):
    """write your data layer access logic here. keep it raw!"""
    name = "employee"
    model = Employee # your sqlalchemy model, support for more orm is coming soon
```
2️⃣ Just call `FastapiListing`

```python
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session

# import fastapi-listing dependencies
from fastapi_listing.paginator import ListingPage # default pydantic page model, you can create/extend your own
from fastapi_listing import FastapiListing, MetaInfo

app = FastAPI()  # create FastAPI app

def get_db() -> Session:
    """
    replicating sessionmaker for any fastapi app.
    anyone could be using a different way or opensource packages to produce sessions.
    it all comes down to a single result that is yielding a session.
    for the sake of simplicity and testing purpose I'm replicating this behaviour in this way.
    :return: Session
    """



# your pydantic response class if you are using one
# Supports pydantic v2
class EmployeeListDetails(BaseModel):
    emp_no: int = Field(alias="empid", title="Employee ID")
    birth_date: date = Field(alias="bdt", title="Birth Date")
    first_name: str = Field(alias="fnm", title="First Name")
    last_name: str = Field(alias="lnm", title="Last Name")
    gender: str = Field(alias="gdr", title="Gender")
    hire_date: date = Field(alias="hdt", title="Hiring Date")

    class Config:
        orm_mode = True
        allow_population_by_field_name = True
    
@app.get("/employees", response_model=ListingPage[EmployeeListDetails])
def get_employees(db=Depends(get_db)):
    dao = EmployeeDao(read_db=db)
    # passing pydantic serializer is optional, automatically generates a
    # select query based on pydantic class fields for easy cases like columns of same table
    return FastapiListing(dao=dao, pydantic_serializer=EmployeeListDetails
                          ).get_response(MetaInfo(default_srt_on="emp_no"))    
```

Voila 🎉 your very first listing response(that's even extensible user can manipulate default page structure)

![](https://drive.google.com/uc?export=view&id=1amgrAdGP7WvXfiNlCYJZPC9fz4_1CidE)


Your pydantic class contains some dynamic fields that you populate at runtime❓️

```python
@app.get("/employees", response_model=ListingPage[EmployeeListDetails])
def get_employees(db=Depends(get_db)):
    dao = EmployeeDao(read_db=db)
    return FastapiListing(dao=dao,
                          pydantic_serializer=EmployeeListDetails, # optional
                          custom_fields=True # just tell fastapi-listing that your model contains custom_fields
                          ).get_response(MetaInfo(default_srt_on="emp_no"))
```

Auto generated query  doesn't fulfil your use case❓️

```python
class EmployeeDao(GenericDao):
    """write your data layer access logic here. keep it raw!"""
    name = "employee"
    model = Employee
    
    def get_default_read(self, fields_to_read: Optional[list]):
        """
        Extend and return your query from here.
        Use it when use cases are comparatively easier than complex.
        Alternatively fastapi-listing provides a robust way to write performance packed queries 
        for complex APIs which we will look at later.
        """
        query = self._read_db.query(Employee)
        return query
```

```python
@app.get("/employees", response_model=ListingPage[EmployeeListDetails])
def get_employees(db=Depends(get_db)):
    dao = EmployeeDao(read_db=db)
    return FastapiListing(dao=dao).get_response(MetaInfo(default_srt_on="emp_no"))
```


## Thinking about adding filters???
Don't worry I've got you covered😎

➡️ Say you want to add filter on Employee for:
1.  gender - return only **Employees** belonging to 'X' gender where X could be anything.
2.  DOB - return **Employees** belonging to a specific range of DOB.
3.  First Name - return **Employees** only starting with specific first names.
```python
from fastapi_listing.filters import generic_filters # collection of inbuilt filters
from fastapi_listing.factory import filter_factory # import filter_factory to register filter against a listing

# {"alias": ("<model.field>", "<filter_definition>")}
emp_filter_mapper = {
    "gdr": ("Employee.gender", generic_filters.EqualityFilter),
    "bdt": ("Employee.birth_date", generic_filters.MySqlNativeDateFormateRangeFilter),
    "fnm": ("Employee.first_name", generic_filters.StringStartsWithFilter),
}
filter_factory.register_filter_mapper(emp_filter_mapper) # You just registered the number of filters allowed to client on this listing


@app.get("/employees", response_model=ListingPage[EmployeeListDetails])
def get_employees(request: Request, db=Depends(get_db)):
    """
    request is optional to pass.
    you can pass filter query_param or use request object.
    make fastapi-listing adapt to your client existing query_param format
    """
    dao = EmployeeDao(read_db=db)
    return FastapiListing(request=request, dao=dao).get_response(
        MetaInfo(default_srt_on="emp_no", filter_mapper=emp_filter_mapper))
    
```

Thinking about how fastapi-listing reads filter/sorter/paginator params❓️

```python
# Extend adapter to make fastapi-listing adapt your existing clients

# default implementation

from fastapi_listing.service.adapters import CoreListingParamsAdapter

class YourAdapterClass(CoreListingParamsAdapter): # Extend to add your behaviour
    """Utilise this adapter class to make your remote client site:
    - filter,
    - sorter,
    - paginator.
    query params adapt to fastapi listing library.
    With this you can utilise same listing api to multiple remote client
    even if it's a front end server or other backend server.

    core service is always going to request one of the following fundamental key
    - sort
    - filter
    - pagination
    depending upon this return the appropriate transformed client param back to fastapi listing
    supported formats for
    filter:
    simple filter - [{"field":"<key used in filter mapper>", "value":{"search":"<client param>"}}, ...]
    if you are using a range filter -
    [{"field":"<key used in filter mapper>", "value":{"start":"<start range>", "end": "<end range>"}}, ...]
    if you are using a list filter i.e. search on given items
    [{"field":"<key used in filter mapper>", "value":{"list":["<client params>"]}}, ...]

    sort:
    [{"field":<"key used in sort mapper>", "type":"asc or "dsc"}, ...]
    by default single sort allowed you can change it by extending sort interceptor

    pagination:
    {"pageSize": <integer page size>, "page": <integer page number 1 based>}
    """
    
    def get(self, key: Literal["sort", "filter", "pagination"]):
        """
        @param key: Literal["sort", "filter", "pagination"]
        @return: List[Optional[dict]] for filter/sort and dict for paginator
        """
        return utils.dictify_query_params(self.dependency.get(key))

```
Pass request or extract feature(filter/sorter/paginator) params at router and pass them as kwargs

```python

@app.get("/employees", response_model=ListingPage[EmployeeListDetails])
def get_employees(request: Request, db=Depends(get_db)):
    params = request.query_params
    # filter, sort. pagination = params.get("filter"), params.get("sort"), params.get("paginator")
    # you can pass above args as kwargs in MetaInfo
    dao = EmployeeDao(read_db=db)
    return FastapiListing(request=request, dao=dao).get_response(
        MetaInfo(default_srt_on="emp_no", filter_mapper=emp_filter_mapper, feature_params_adapter=YourAdapterClass))
    
```

Check out [docs](https://fastapi-listing.readthedocs.io/en/latest/tutorials.html#adding-filters-to-your-listing-api) for supported list of filters.
Additionally, you can create **custom filters** as well.

## Thinking about adding Sorting???
I won't leave you hanging there as well😎
```python
@app.get("/employees", response_model=ListingPage[EmployeeListDetails])
def get_employees(request: Request, db=Depends(get_db)):
    # define it here or anywhere
    emp_sort_mapper = {
        "cd": "Employee.emp_no",
        "bdt": "Employee.birth_date"
    }
    dao = EmployeeDao(read_db=db)
    return FastapiListing(request=request, dao=dao).get_response(
        MetaInfo(default_srt_on="emp_no", filter_mapper=emp_filter_mapper, feature_params_adapter=YourAdapterClass,
                 sort_mapper=emp_sort_mapper))
```

## Provided features are not meeting your requirements???

It is customizable.😎

➡️ You can write custom:

* Query
* Filter
* Sorter
* Paginator

You can check out customisation section in docs after going through basics and tutorials.

Check out my other [repo](https://github.com/danielhasan1/test-fastapi-listing/blob/master/app/router/router.py) to see some examples

## Features and Readability hand in hand 🤝

 - Easy-to-use API for listing and formatting data
 - Built-in support for pagination, sorting and filtering
 - Well defined interface for filter, sorter, paginator
 - Support Dependency Injection for easy testing
 - Room to adapt the existing remote client query param semantics
 - Write standardise listing APIs that will be understood by generations of upcoming developers
 - Write listing features which is easy on human mind to extend or understand
 - Break down the most complex listing data APIs into digestible piece of code 

With FastAPI Listing you won't end up like

<img src="https://drive.google.com/uc?export=view&id=1C2ZHltxpdyq4YmBsnbOu4HF9JGt6uMfQ" width="600" height="600"/>

# Documentation
View full documentation at: https://fastapi-listing.readthedocs.io ███▓▓░️░️░35%️░️░️░️



# Feedback, Questions?

Any form of feedback and questions are welcome! Please create an issue  💭
[here](https://github.com/danielhasan1/fastapi-listing/issues/new).

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/danielhasan1/fastapi-listing",
    "name": "fastapi-listing",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "starlette,fastapi,pydantic,sqlalchemy",
    "author": "Danish Hasan",
    "author_email": "dh813030@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/49/de/8c96dcb1a60547e96867366f340b22015780c982ace074a569fd4b2cc725/fastapi-listing-0.3.2.tar.gz",
    "platform": null,
    "description": "# fastapi-listing\n\nAdvanced items listing library that gives you freedom to design complex listing APIs that can be read by human.\n\n[![.github/workflows/deploy.yml](https://github.com/danielhasan1/fastapi-listing/actions/workflows/deploy.yml/badge.svg)](https://github.com/danielhasan1/fastapi-listing/actions/workflows/deploy.yml)\n[![.github/workflows/tests.yml](https://github.com/danielhasan1/fastapi-listing/actions/workflows/tests.yml/badge.svg)](https://github.com/danielhasan1/fastapi-listing/actions/workflows/tests.yml) ![PyPI - Programming Language](https://img.shields.io/pypi/pyversions/fastapi-listing.svg?color=%2334D058)\n[![codecov](https://codecov.io/gh/danielhasan1/fastapi-listing/branch/dev/graph/badge.svg?token=U29ZRNAH8I)](https://codecov.io/gh/danielhasan1/fastapi-listing) [![Downloads](https://static.pepy.tech/badge/fastapi-listing)](https://pepy.tech/project/fastapi-listing)\n\n\u27a1\ufe0f Craft powerful Listing REST APIs designed to serve websites akin to Stack Overflow:\n\n![](https://drive.google.com/uc?export=view&id=1sCkzxi7OirmtA9gGM0LlK9dryI1dlU4U)\n\nComes with:\n- pre defined filters\n- pre defined paginator\n- pre defined sorter\n\n## Advantage\n- simplify the intricate process of designing and developing complex listing APIs\n- Design components(USP) and plug them from anywhere\n- Components can be **reusable**\n- Best for fast changing needs\n\n## Usage\n\n\u27a1\ufe0f With the New Compact Version(Older version was provided with a guide style which is supported by this version as well)\ncreate your listing API in 2 easy steps \ud83e\udd73\n\n1\ufe0f\u20e3 Create a Dao (Data Access Object) layer (a simple class)\n\n```python\nfrom fastapi_listing.dao import GenericDao\nfrom app.model import Employee\n\n\n# your dao (data access object) placed here for the sake of example\nclass EmployeeDao(GenericDao):\n    \"\"\"write your data layer access logic here. keep it raw!\"\"\"\n    name = \"employee\"\n    model = Employee # your sqlalchemy model, support for more orm is coming soon\n```\n2\ufe0f\u20e3 Just call `FastapiListing`\n\n```python\nfrom fastapi import FastAPI, Depends\nfrom sqlalchemy.orm import Session\n\n# import fastapi-listing dependencies\nfrom fastapi_listing.paginator import ListingPage # default pydantic page model, you can create/extend your own\nfrom fastapi_listing import FastapiListing, MetaInfo\n\napp = FastAPI()  # create FastAPI app\n\ndef get_db() -> Session:\n    \"\"\"\n    replicating sessionmaker for any fastapi app.\n    anyone could be using a different way or opensource packages to produce sessions.\n    it all comes down to a single result that is yielding a session.\n    for the sake of simplicity and testing purpose I'm replicating this behaviour in this way.\n    :return: Session\n    \"\"\"\n\n\n\n# your pydantic response class if you are using one\n# Supports pydantic v2\nclass EmployeeListDetails(BaseModel):\n    emp_no: int = Field(alias=\"empid\", title=\"Employee ID\")\n    birth_date: date = Field(alias=\"bdt\", title=\"Birth Date\")\n    first_name: str = Field(alias=\"fnm\", title=\"First Name\")\n    last_name: str = Field(alias=\"lnm\", title=\"Last Name\")\n    gender: str = Field(alias=\"gdr\", title=\"Gender\")\n    hire_date: date = Field(alias=\"hdt\", title=\"Hiring Date\")\n\n    class Config:\n        orm_mode = True\n        allow_population_by_field_name = True\n    \n@app.get(\"/employees\", response_model=ListingPage[EmployeeListDetails])\ndef get_employees(db=Depends(get_db)):\n    dao = EmployeeDao(read_db=db)\n    # passing pydantic serializer is optional, automatically generates a\n    # select query based on pydantic class fields for easy cases like columns of same table\n    return FastapiListing(dao=dao, pydantic_serializer=EmployeeListDetails\n                          ).get_response(MetaInfo(default_srt_on=\"emp_no\"))    \n```\n\nVoila \ud83c\udf89 your very first listing response(that's even extensible user can manipulate default page structure)\n\n![](https://drive.google.com/uc?export=view&id=1amgrAdGP7WvXfiNlCYJZPC9fz4_1CidE)\n\n\nYour pydantic class contains some dynamic fields that you populate at runtime\u2753\ufe0f\n\n```python\n@app.get(\"/employees\", response_model=ListingPage[EmployeeListDetails])\ndef get_employees(db=Depends(get_db)):\n    dao = EmployeeDao(read_db=db)\n    return FastapiListing(dao=dao,\n                          pydantic_serializer=EmployeeListDetails, # optional\n                          custom_fields=True # just tell fastapi-listing that your model contains custom_fields\n                          ).get_response(MetaInfo(default_srt_on=\"emp_no\"))\n```\n\nAuto generated query  doesn't fulfil your use case\u2753\ufe0f\n\n```python\nclass EmployeeDao(GenericDao):\n    \"\"\"write your data layer access logic here. keep it raw!\"\"\"\n    name = \"employee\"\n    model = Employee\n    \n    def get_default_read(self, fields_to_read: Optional[list]):\n        \"\"\"\n        Extend and return your query from here.\n        Use it when use cases are comparatively easier than complex.\n        Alternatively fastapi-listing provides a robust way to write performance packed queries \n        for complex APIs which we will look at later.\n        \"\"\"\n        query = self._read_db.query(Employee)\n        return query\n```\n\n```python\n@app.get(\"/employees\", response_model=ListingPage[EmployeeListDetails])\ndef get_employees(db=Depends(get_db)):\n    dao = EmployeeDao(read_db=db)\n    return FastapiListing(dao=dao).get_response(MetaInfo(default_srt_on=\"emp_no\"))\n```\n\n\n## Thinking about adding filters???\nDon't worry I've got you covered\ud83d\ude0e\n\n\u27a1\ufe0f Say you want to add filter on Employee for:\n1.  gender - return only **Employees** belonging to 'X' gender where X could be anything.\n2.  DOB - return **Employees** belonging to a specific range of DOB.\n3.  First Name - return **Employees** only starting with specific first names.\n```python\nfrom fastapi_listing.filters import generic_filters # collection of inbuilt filters\nfrom fastapi_listing.factory import filter_factory # import filter_factory to register filter against a listing\n\n# {\"alias\": (\"<model.field>\", \"<filter_definition>\")}\nemp_filter_mapper = {\n    \"gdr\": (\"Employee.gender\", generic_filters.EqualityFilter),\n    \"bdt\": (\"Employee.birth_date\", generic_filters.MySqlNativeDateFormateRangeFilter),\n    \"fnm\": (\"Employee.first_name\", generic_filters.StringStartsWithFilter),\n}\nfilter_factory.register_filter_mapper(emp_filter_mapper) # You just registered the number of filters allowed to client on this listing\n\n\n@app.get(\"/employees\", response_model=ListingPage[EmployeeListDetails])\ndef get_employees(request: Request, db=Depends(get_db)):\n    \"\"\"\n    request is optional to pass.\n    you can pass filter query_param or use request object.\n    make fastapi-listing adapt to your client existing query_param format\n    \"\"\"\n    dao = EmployeeDao(read_db=db)\n    return FastapiListing(request=request, dao=dao).get_response(\n        MetaInfo(default_srt_on=\"emp_no\", filter_mapper=emp_filter_mapper))\n    \n```\n\nThinking about how fastapi-listing reads filter/sorter/paginator params\u2753\ufe0f\n\n```python\n# Extend adapter to make fastapi-listing adapt your existing clients\n\n# default implementation\n\nfrom fastapi_listing.service.adapters import CoreListingParamsAdapter\n\nclass YourAdapterClass(CoreListingParamsAdapter): # Extend to add your behaviour\n    \"\"\"Utilise this adapter class to make your remote client site:\n    - filter,\n    - sorter,\n    - paginator.\n    query params adapt to fastapi listing library.\n    With this you can utilise same listing api to multiple remote client\n    even if it's a front end server or other backend server.\n\n    core service is always going to request one of the following fundamental key\n    - sort\n    - filter\n    - pagination\n    depending upon this return the appropriate transformed client param back to fastapi listing\n    supported formats for\n    filter:\n    simple filter - [{\"field\":\"<key used in filter mapper>\", \"value\":{\"search\":\"<client param>\"}}, ...]\n    if you are using a range filter -\n    [{\"field\":\"<key used in filter mapper>\", \"value\":{\"start\":\"<start range>\", \"end\": \"<end range>\"}}, ...]\n    if you are using a list filter i.e. search on given items\n    [{\"field\":\"<key used in filter mapper>\", \"value\":{\"list\":[\"<client params>\"]}}, ...]\n\n    sort:\n    [{\"field\":<\"key used in sort mapper>\", \"type\":\"asc or \"dsc\"}, ...]\n    by default single sort allowed you can change it by extending sort interceptor\n\n    pagination:\n    {\"pageSize\": <integer page size>, \"page\": <integer page number 1 based>}\n    \"\"\"\n    \n    def get(self, key: Literal[\"sort\", \"filter\", \"pagination\"]):\n        \"\"\"\n        @param key: Literal[\"sort\", \"filter\", \"pagination\"]\n        @return: List[Optional[dict]] for filter/sort and dict for paginator\n        \"\"\"\n        return utils.dictify_query_params(self.dependency.get(key))\n\n```\nPass request or extract feature(filter/sorter/paginator) params at router and pass them as kwargs\n\n```python\n\n@app.get(\"/employees\", response_model=ListingPage[EmployeeListDetails])\ndef get_employees(request: Request, db=Depends(get_db)):\n    params = request.query_params\n    # filter, sort. pagination = params.get(\"filter\"), params.get(\"sort\"), params.get(\"paginator\")\n    # you can pass above args as kwargs in MetaInfo\n    dao = EmployeeDao(read_db=db)\n    return FastapiListing(request=request, dao=dao).get_response(\n        MetaInfo(default_srt_on=\"emp_no\", filter_mapper=emp_filter_mapper, feature_params_adapter=YourAdapterClass))\n    \n```\n\nCheck out [docs](https://fastapi-listing.readthedocs.io/en/latest/tutorials.html#adding-filters-to-your-listing-api) for supported list of filters.\nAdditionally, you can create **custom filters** as well.\n\n## Thinking about adding Sorting???\nI won't leave you hanging there as well\ud83d\ude0e\n```python\n@app.get(\"/employees\", response_model=ListingPage[EmployeeListDetails])\ndef get_employees(request: Request, db=Depends(get_db)):\n    # define it here or anywhere\n    emp_sort_mapper = {\n        \"cd\": \"Employee.emp_no\",\n        \"bdt\": \"Employee.birth_date\"\n    }\n    dao = EmployeeDao(read_db=db)\n    return FastapiListing(request=request, dao=dao).get_response(\n        MetaInfo(default_srt_on=\"emp_no\", filter_mapper=emp_filter_mapper, feature_params_adapter=YourAdapterClass,\n                 sort_mapper=emp_sort_mapper))\n```\n\n## Provided features are not meeting your requirements???\n\nIt is customizable.\ud83d\ude0e\n\n\u27a1\ufe0f You can write custom:\n\n* Query\n* Filter\n* Sorter\n* Paginator\n\nYou can check out customisation section in docs after going through basics and tutorials.\n\nCheck out my other [repo](https://github.com/danielhasan1/test-fastapi-listing/blob/master/app/router/router.py) to see some examples\n\n## Features and Readability hand in hand \ud83e\udd1d\n\n - Easy-to-use API for listing and formatting data\n - Built-in support for pagination, sorting and filtering\n - Well defined interface for filter, sorter, paginator\n - Support Dependency Injection for easy testing\n - Room to adapt the existing remote client query param semantics\n - Write standardise listing APIs that will be understood by generations of upcoming developers\n - Write listing features which is easy on human mind to extend or understand\n - Break down the most complex listing data APIs into digestible piece of code \n\nWith FastAPI Listing you won't end up like\n\n<img src=\"https://drive.google.com/uc?export=view&id=1C2ZHltxpdyq4YmBsnbOu4HF9JGt6uMfQ\" width=\"600\" height=\"600\"/>\n\n# Documentation\nView full documentation at: https://fastapi-listing.readthedocs.io \u2588\u2588\u2588\u2593\u2593\u2591\ufe0f\u2591\ufe0f\u259135%\ufe0f\u2591\ufe0f\u2591\ufe0f\u2591\ufe0f\n\n\n\n# Feedback, Questions?\n\nAny form of feedback and questions are welcome! Please create an issue  \ud83d\udcad\n[here](https://github.com/danielhasan1/fastapi-listing/issues/new).\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Advaned Data Listing Library for FastAPI",
    "version": "0.3.2",
    "project_urls": {
        "Homepage": "https://github.com/danielhasan1/fastapi-listing"
    },
    "split_keywords": [
        "starlette",
        "fastapi",
        "pydantic",
        "sqlalchemy"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "14c18a65c2f4ac6588bb4a761438ce5e69d19d1c4e94881f38767c3084dae140",
                "md5": "aa3af02c7e4390306e4b8eb433b2d178",
                "sha256": "6b2ddcbfc5b4a60877e9d8e3eda112d6bdd341fd4f35890d7d36caeecdb18780"
            },
            "downloads": -1,
            "filename": "fastapi_listing-0.3.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "aa3af02c7e4390306e4b8eb433b2d178",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 52040,
            "upload_time": "2024-01-30T10:49:33",
            "upload_time_iso_8601": "2024-01-30T10:49:33.872622Z",
            "url": "https://files.pythonhosted.org/packages/14/c1/8a65c2f4ac6588bb4a761438ce5e69d19d1c4e94881f38767c3084dae140/fastapi_listing-0.3.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "49de8c96dcb1a60547e96867366f340b22015780c982ace074a569fd4b2cc725",
                "md5": "f03c905c43ac94392587e2d58119facd",
                "sha256": "5535a9aff110e38c1ca830ceac08d159330bb3a1bdb14bcd16bd3c9a9c4704ae"
            },
            "downloads": -1,
            "filename": "fastapi-listing-0.3.2.tar.gz",
            "has_sig": false,
            "md5_digest": "f03c905c43ac94392587e2d58119facd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 40766,
            "upload_time": "2024-01-30T10:49:35",
            "upload_time_iso_8601": "2024-01-30T10:49:35.731193Z",
            "url": "https://files.pythonhosted.org/packages/49/de/8c96dcb1a60547e96867366f340b22015780c982ace074a569fd4b2cc725/fastapi-listing-0.3.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-30 10:49:35",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "danielhasan1",
    "github_project": "fastapi-listing",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "fastapi-listing"
}
        
Elapsed time: 0.19303s