{"id":24703005,"url":"https://github.com/jsonzilla/template_fastapi_mongodb","last_synced_at":"2026-04-09T21:44:44.568Z","repository":{"id":46235664,"uuid":"514074986","full_name":"jsonzilla/template_fastapi_mongodb","owner":"jsonzilla","description":"Template for rapid prototyping with FastAPI and MongoDB","archived":false,"fork":false,"pushed_at":"2023-05-04T21:06:21.000Z","size":89,"stargazers_count":1,"open_issues_count":6,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-27T05:51:45.840Z","etag":null,"topics":["fastapi","mongodb","template"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsonzilla.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null},"funding":{"github":"jsonzilla"}},"created_at":"2022-07-14T23:26:54.000Z","updated_at":"2023-10-23T20:13:26.000Z","dependencies_parsed_at":"2023-01-23T19:31:06.082Z","dependency_job_id":null,"html_url":"https://github.com/jsonzilla/template_fastapi_mongodb","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonzilla%2Ftemplate_fastapi_mongodb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonzilla%2Ftemplate_fastapi_mongodb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonzilla%2Ftemplate_fastapi_mongodb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonzilla%2Ftemplate_fastapi_mongodb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsonzilla","download_url":"https://codeload.github.com/jsonzilla/template_fastapi_mongodb/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244905563,"owners_count":20529648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastapi","mongodb","template"],"created_at":"2025-01-27T05:51:50.994Z","updated_at":"2026-04-09T21:44:39.550Z","avatar_url":"https://github.com/jsonzilla.png","language":"Python","funding_links":["https://github.com/sponsors/jsonzilla"],"categories":[],"sub_categories":[],"readme":"# Template project FastAPI with MongoDB\n\n## Install the requirements:\nA mongo database is required to run the server.\nGet one from [MongoDB Atlas](https://www.mongodb.com/cloud/atlas), for free.\n```bash\npip install -r requirements.txt\n```\n\n## Run locally\n```bash\nuvicorn app.main:app --reload\n```\nTo see the Swagger documentation, visit:\nhttp://localhost:8000/docs\n\n### Test\n```bash\npython -m pytest -W ignore::DeprecationWarning\npytest --cov=app --cov-report=html\npython -m pytest_watch\n```\n\n### Test watch\n```bash\nptw\n```\n\n## [Config .env](https://fastapi.tiangolo.com/advanced/settings/#reading-a-env-file)\nConfigure the location of your MongoDB database in a .env file:\n```\nMONGO_URL=\"mongodb://\u003cusername\u003e:\u003cpassword\u003e@\u003curl\u003e/\u003cdb\u003e?retryWrites=true\u0026w=majority\"\n```\n\n\n\n# Steps to create a new model\nUsing the tools below, you can create a new model.\n**Tools**:\n* [jsontopydantic](https://jsontopydantic.com/)\n* [datamodel-code-generator](https://koxudaxi.github.io/datamodel-code-generator/)\n\n## 1. Define a new model\nJson base for our model:\n```json\n{\n    \"name\": \"Jack of all trades\",\n    \"age\": \"42\",\n    \"occupation\": \"King of the world\",\n    \"hobbies\": [\n        \"Sleeping\",\n        \"Eating\",\n        \"Being a jack of all trades\"\n    ],\n    \"friends\": [\n        {\"name\": \"Jill\", \"age\": \"42\", \"occupation\": \"King of another world\"},\n        {\"name\": \"Jane\", \"age\": \"50\", \"occupation\": \"Queen of another world\"}\n    ],\n    \"created_at\": \"2020-01-01T00:00:00.000Z\",\n    \"last_update\": \"2020-01-01T00:00:00.000Z\"\n}\n```\nCopy this example to your project folder ```models/examples``` and rename it to ```\u003cmodel_name\u003e_example.py```.\nThis will be user for swagger documentation and unit tests.\n```python\nexample = ...code above...\nexample_update = example\n```\n\n\n### 1.1 Generate the model with the tools:\nuse the site or datamodel-code-generator to generate all models from json. **Tool**: [jsontopydantic](https://jsontopydantic.com/)\n\n```python\nfrom __future__ import annotations\n\nfrom typing import List\n\nfrom pydantic import BaseModel\n\n\nclass Friend(BaseModel):\n    name: str\n    age: str\n    occupation: str\n\n\nclass Model(BaseModel):\n    name: str\n    age: str\n    occupation: str\n    hobbies: List[str]\n    friends: List[Friend]\n    created_at: str\n    last_update: str\n```\n\n\n## 2. Create the model in project\nIn models folder, create a new file with the name of the model and the extension .py.\nIn this example the file name is ```\u003cmodel_name\u003e_model.py```..\n\n### Import to the librarys:\n```python\nfrom datetime import datetime\nfrom typing import List, Optional\nfrom pydantic import BaseModel, Field\nfrom bson.objectid import ObjectId\nfrom app.codecs import ObjectIdCodec\nfrom app.models.examples.person_example import example, example_update\n```\n\n### 2.1 Copy the models and format the code:\nCreate the models, add the fields and the validators.\n```python\nclass Friend(BaseModel):\n    name: str = Field(description='Name of the friend', min_length=1, max_length=100)\n    age: Optional[int] = Field(description='Age of the friend', ge=0)\n    occupation: Optional[str] = Field(description='Occupation of the friend', min_length=1, max_length=255)\n\n\nclass PersonModel(BaseModel):\n    id: ObjectIdCodec = Field(default_factory=ObjectIdCodec, alias=\"_id\")\n    name: str = Field(description=\"The name of the person\", min_length=1, max_length=255)\n    age: int = Field(description=\"The age of the person\", gt=0)\n    occupation: str = Field(description=\"The occupation of the person\", min_length=1, max_length=255)\n    hobbies: List[str] = Field(description=\"The hobbies of the person\")\n    friends: List[Friend] = Field(description=\"The friends of the person\")\n    created_at: datetime = Field(description=\"The creation date of the person\")\n    last_update: datetime = Field(description=\"The last update of the person\")\n\n    class Config:\n        allow_population_by_field_name = True\n        arbitrary_types_allowed = True\n        anystr_strip_whitespace = True\n        json_encoders = {ObjectId: str, datetime: str}\n        schema_extra = {\"example\": example}\n\n\nclass PersonUpdateModel(BaseModel):\n    name: Optional[str] = Field(description=\"The name of the person\", min_length=1, max_length=255)\n    age: Optional[str] = Field(description=\"The age of the person\", min_length=1, max_length=255)\n    occupation: Optional[str] = Field(description=\"The occupation of the person\", min_length=1, max_length=255)\n    hobbies: Optional[List[str]] = Field(description=\"The hobbies of the person\")\n    friends: Optional[List[Friend]] = Field(description=\"The friends of the person\")\n\n    class Config:\n        allow_population_by_field_name = True\n        arbitrary_types_allowed = True\n        anystr_strip_whitespace = True\n        json_encoders = {ObjectId: str, datetime: str}\n        schema_extra = {\"example\": example_update}\n```\n\n### 2.2 Create additional model for update:\n```python\nclass PersonFilterModel(BaseModel):\n    name: Optional[str] = Field(description=\"The name of the person\", min_length=1, max_length=255)\n    age: Optional[str] = Field(description=\"The age of the person\", min_length=1, max_length=255)\n    occupation: Optional[str] = Field(description=\"The occupation of the person\", min_length=1, max_length=255)\n    hobby: Optional[str] = Field(description=\"The hobby of the person\")\n    friend_name: Optional[str] = Field(description=\"The friends of the person\")\n    created_at: Optional[datetime] = Field(description=\"The creation date of the person\")\n    last_update: Optional[datetime] = Field(description=\"The last update of the person\")\n\n    class Config:\n        allow_population_by_field_name = True\n        arbitrary_types_allowed = True\n        anystr_strip_whitespace = True\n        json_encoders = {ObjectId: str, datetime: str}\n```\n\n\n### 2.3 Filter model\nCreate another model, for correct capture nested fields py query string:\nExample: ```https://example.com/api/v1/persons?name=John\u0026age=42```\n\n```python\nclass PersonFilterModel(BaseModel):\n    name: Optional[str] = Field(description=\"The name of the person\", min_length=1, max_length=255)\n    age: Optional[str] = Field(description=\"The age of the person\", min_length=1, max_length=255)\n    occupation: Optional[str] = Field(description=\"The occupation of the person\", min_length=1, max_length=255)\n    hobby: Optional[str] = Field(description=\"The hobby of the person\")\n    friend_name: Optional[str] = Field(description=\"The friends of the person\")\n    created_at: Optional[datetime] = Field(description=\"The creation date of the person\")\n    last_update: Optional[datetime] = Field(description=\"The last update of the person\")\n\n    class Config:\n        allow_population_by_field_name = True\n        arbitrary_types_allowed = True\n        anystr_strip_whitespace = True\n        json_encoders = {ObjectId: str, datetime: str}\n```\n\n### 2.4 Converter\nCreate a converter for the update model:\n```python\ndef filter_to_nested_model(update_model: PersonFilterModel) -\u003e PersonUpdateModel:\n    if update_model.hobby:\n        hobbies = [update_model.hobby]\n    else:\n        hobbies = []\n\n    return PersonUpdateModel(\n        name=update_model.name,\n        age=update_model.age,\n        occupation=update_model.occupation,\n        hobbies=hobbies\n    )  # type: ignore\n```\n\n### 3 Repository\nCreate a repository for the model, with a new file in the repository folder:\n```python\nfrom app.repositories.base_repository import BaseRepository\n\n\nclass PersonRepository(BaseRepository):\n    def __init__(self):\n        super().__init__('_person_collection')\n```\n\n### 4 Route\nCreate the route:\nIn router folder create a file called ```\u003cmodel_name\u003e_route.py```:\n\nThe imports examples:\n```python\nfrom app.storages.database_filter import format_to_database_filter\nfrom fastapi import APIRouter, Depends, Body\nfrom typing import List\nfrom app.storages.database_storage import Database, get_db\nfrom app.models.person_model import PersonModel, PersonUpdateModel, filter_to_nested_model, PersonFilterModel\nfrom app.repositories.person_repository import PersonRepository\nfrom app.routes import BasicRoutable\n```\n\nChange the variables below:\n```python\n_SHOW_NAME = \"person\"\nrouter = APIRouter(\n    prefix=f\"/v1/{_SHOW_NAME}\",\n    tags=[_SHOW_NAME],\n    responses={404: {\"description\": \"Not found\"}}\n)\n_ROUTER = BasicRoutable(PersonRepository(), _SHOW_NAME)\n_MODEL = PersonModel\n_UPDATE_MODEL = PersonUpdateModel\n_FILTER = PersonFilterModel\n```\n\nCopy the code below to the file, to create a generic route:\n```python\n@router.get(\"/\", response_description=f\"List all {_SHOW_NAME}s\", response_model=List[_UPDATE_MODEL])\nasync def list(filter: _FILTER = Depends(_FILTER), db: Database =Depends(get_db)):\n    nested = filter_to_nested_model(filter)\n    db_filter = format_to_database_filter(nested.dict())\n    return await _ROUTER.get_all_by(db_filter, db)\n\n\n@router.get(\"/{id}\", response_description=f\"Get by id {_SHOW_NAME}\", response_model=_UPDATE_MODEL)\nasync def show_by_id(id: str, db: Database =Depends(get_db)):\n    return await _ROUTER.get_by_id(id, db)\n\n\n@router.post(\"/\", response_description=f\"Add new {_SHOW_NAME}\", response_model=_UPDATE_MODEL)\nasync def create(model: _MODEL = Body(...), db: Database =Depends(get_db)):\n    return await _ROUTER.post(model, db)\n\n\n@router.delete(\"/{id}\", response_description=f\"Delete a {_SHOW_NAME}\")\nasync def delete(id: str, db: Database =Depends(get_db)):\n    return await _ROUTER.delete(id, db)\n\n\n@router.patch(\"/{id}\", response_description=f\"Update a {_SHOW_NAME}\", response_model=_UPDATE_MODEL)\nasync def update(id: str, model: _UPDATE_MODEL = Body(...), db: Database =Depends(get_db)):\n    return await _ROUTER.patch(id, model, db)\n```\n\n## 5 Create the tests\nIn tests folder create a file called ```test_\u003cmodel_name\u003e.py```:\n```python\ndef test_read_person(client, id):\n    response = client.get(f'{_BASE_PATH}')\n    assert response.status_code == 200\n    assert response.json() != valid_json\n```\n\n\n## Documentation of libraries\nTo see the documentation, visit:\n* [FastAPI](https://fastapi.tiangolo.com/)\n* [MongoDB](https://www.mongodb.com/)\n* [MongoDB Atlas](https://www.mongodb.com/cloud/atlas)\n* [Pydantic](https://pydantic-docs.helpmanual.io/)\n* [Motor](https://motor.readthedocs.io/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsonzilla%2Ftemplate_fastapi_mongodb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsonzilla%2Ftemplate_fastapi_mongodb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsonzilla%2Ftemplate_fastapi_mongodb/lists"}