Skip to content

Basic example

The documentation is under heavy development!

The documentation is actively being developed and might not cover every feature. For full details, refer to the source code.

Overview

Below is an example of a basic application using CRUD resources. In this example, we define three resources: Material, Rocket, and RocketPart. The Material resource even exposes custom endpoints for updating its name and counting its records.

Implementation

Define basic resources
class Material(CRUDResource):
    code: str = Field(unique=True)
    name: str
    rocket_parts: list['RocketPart'] = Field(default_factory=list)  # (1)!

    @route.post()  # (2)!
    async def update_name(self, name: str) -> str:
        self.name = name
        return f'Material name updated to {name!r}.'

    @route.get()  # (3)!
    @classmethod
    async def count(cls, session: AsyncSessionDep) -> int:
        query = select(func.count()).select_from(cls)
        result = await session.execute(query)
        return result.scalar()

class Rocket(CRUDResource):
    code: str = Field(unique=True)
    name: str
    parts: list['RocketPart'] = Field(default_factory=list)  # (4)!

class RocketPart(CRUDResource):
    __config__ = ConfigDict(indexes=[{'rocket', 'material'}])  # (5)!

    rocket: Rocket  # (6)!
    material: Material
    quantity: int
  1. The rocket_parts field establishes a reverse relationship to RocketPart, enabling bidirectional navigation between materials and their usage in rockets.
  2. The update_name method is a custom route defined at the resource instance level. It updates the name attribute of the Material instance and returns a message confirming the update. It can be accessed at the /materials/{_material_key}/update-name endpoint.
  3. The count method is a custom route defined at the resource class level. It queries the database to count the number of Material instances and returns the result. It can be accessed at the /materials/count endpoint.
  4. The parts field now references RocketPart instead of Material directly, allowing for additional attributes on the relationship.
  5. A composite index ensures uniqueness of material-rocket combinations, preventing duplicate entries.
  6. Forward references using string literals establish proper relationships while avoiding circular imports.
Source code in src/examples/basic/example.py
from pathlib import Path

from plateforme import ConfigDict, CRUDResource, Field, Plateforme
from plateforme.api import AsyncSessionDep, route
from plateforme.database import func, select

dirname = Path(__file__).parent

# Create application
app = Plateforme(
    debug=True,
    logging={'level': 'DEBUG'},
    database_engines=dirname / 'plateforme.db',
)

# Example
class Material(CRUDResource):
    code: str = Field(unique=True)
    name: str
    rocket_parts: list['RocketPart'] = Field(default_factory=list)  # (1)!

    @route.post()  # (2)!
    async def update_name(self, name: str) -> str:
        self.name = name
        return f'Material name updated to {name!r}.'

    @route.get()  # (3)!
    @classmethod
    async def count(cls, session: AsyncSessionDep) -> int:
        query = select(func.count()).select_from(cls)
        result = await session.execute(query)
        return result.scalar()

class Rocket(CRUDResource):
    code: str = Field(unique=True)
    name: str
    parts: list['RocketPart'] = Field(default_factory=list)  # (4)!

class RocketPart(CRUDResource):
    __config__ = ConfigDict(indexes=[{'rocket', 'material'}])  # (5)!

    rocket: Rocket  # (6)!
    material: Material
    quantity: int

# Setup database
if __name__ == '__main__':
    app.metadata.create_all()

Playing with materials

Add some materials

The following example shows how to add multiple materials.

Create some materials
POST http://127.0.0.1:8001/materials HTTP/1.1
content-type: application/json

{
  "payload": [
    {
      "name": "Main Engine",
      "code": "ME-1000"
    },
    {
      "name": "Primary Fuel Tank",
      "code": "FT-500"
    },
    ...
  ]
}
Request
POST http://127.0.0.1:8001/materials HTTP/1.1
content-type: application/json

{
  "payload": [
    {
      "name": "Main Engine",
      "code": "ME-1000"
    },
    {
      "name": "Primary Fuel Tank",
      "code": "FT-500"
    },
    {
      "name": "Secondary Fuel Tank",
      "code": "FT-501"
    },
    {
      "name": "Guidance System",
      "code": "GS-200"
    },
    {
      "name": "Primary Payload Bay",
      "code": "PB-300"
    },
    {
      "name": "Secondary Payload Bay",
      "code": "PB-301"
    },
    {
      "name": "Nose Cone",
      "code": "NC-150"
    },
    {
      "name": "Thrust Vector Control",
      "code": "TV-400"
    },
    {
      "name": "Navigation Computer",
      "code": "NC-600"
    },
    {
      "name": "Communication System",
      "code": "CS-700"
    },
    {
      "name": "Thermal Protection System",
      "code": "TP-800"
    },
    {
      "name": "Landing Gear",
      "code": "LG-250"
    },
    {
      "name": "Power Distribution Unit",
      "code": "PD-900"
    },
    {
      "name": "Life Support System",
      "code": "LS-350"
    },
    {
      "name": "Emergency Escape System",
      "code": "EE-450"
    },
    {
      "name": "Oxidizer Tank",
      "code": "OT-550"
    }
  ]
}
Response
[
  {
    "id": 1,
    "type": "material",
    "code": "ME-1000",
    "name": "Main Engine",
    "rocket_parts": []
  },
  {
    "id": 2,
    "type": "material",
    "code": "FT-500",
    "name": "Primary Fuel Tank",
    "rocket_parts": []
  },
  {
    "id": 3,
    "type": "material",
    "code": "FT-501",
    "name": "Secondary Fuel Tank",
    "rocket_parts": []
  },
  {
    "id": 4,
    "type": "material",
    "code": "GS-200",
    "name": "Guidance System",
    "rocket_parts": []
  },
  {
    "id": 5,
    "type": "material",
    "code": "PB-300",
    "name": "Primary Payload Bay",
    "rocket_parts": []
  },
  {
    "id": 6,
    "type": "material",
    "code": "PB-301",
    "name": "Secondary Payload Bay",
    "rocket_parts": []
  },
  {
    "id": 7,
    "type": "material",
    "code": "NC-150",
    "name": "Nose Cone",
    "rocket_parts": []
  },
  {
    "id": 8,
    "type": "material",
    "code": "TV-400",
    "name": "Thrust Vector Control",
    "rocket_parts": []
  },
  {
    "id": 9,
    "type": "material",
    "code": "NC-600",
    "name": "Navigation Computer",
    "rocket_parts": []
  },
  {
    "id": 10,
    "type": "material",
    "code": "CS-700",
    "name": "Communication System",
    "rocket_parts": []
  },
  {
    "id": 11,
    "type": "material",
    "code": "TP-800",
    "name": "Thermal Protection System",
    "rocket_parts": []
  },
  {
    "id": 12,
    "type": "material",
    "code": "LG-250",
    "name": "Landing Gear",
    "rocket_parts": []
  },
  {
    "id": 13,
    "type": "material",
    "code": "PD-900",
    "name": "Power Distribution Unit",
    "rocket_parts": []
  },
  {
    "id": 14,
    "type": "material",
    "code": "LS-350",
    "name": "Life Support System",
    "rocket_parts": []
  },
  {
    "id": 15,
    "type": "material",
    "code": "EE-450",
    "name": "Emergency Escape System",
    "rocket_parts": []
  },
  {
    "id": 16,
    "type": "material",
    "code": "OT-550",
    "name": "Oxidizer Tank",
    "rocket_parts": []
  }
]

Fetch materials

You can sort or filter the list of materials. For example, to retrieve materials sorted by code:

Fetch materials with sorting and limit
GET http://127.0.0.1:8001/materials?limit=5&sort=-code HTTP/1.1
Response
[
  {
    "id": 8,
    "type": "material",
    "code": "TV-400",
    "name": "Thrust Vector Control",
    "rocket_parts": []
  },
  {
    "id": 11,
    "type": "material",
    "code": "TP-800",
    "name": "Thermal Protection System",
    "rocket_parts": []
  },
  {
    "id": 13,
    "type": "material",
    "code": "PD-900",
    "name": "Power Distribution Unit",
    "rocket_parts": []
  },
  {
    "id": 6,
    "type": "material",
    "code": "PB-301",
    "name": "Secondary Payload Bay",
    "rocket_parts": []
  },
  {
    "id": 5,
    "type": "material",
    "code": "PB-300",
    "name": "Primary Payload Bay",
    "rocket_parts": []
  }
]

Filter materials

Or, to filter materials whose code matches a pattern:

Fetch materials that match specific conditions
GET http://127.0.0.1:8001/materials?.code=like~*NC*&.id=gt~2 HTTP/1.1
Response
[
  {
    "id": 7,
    "type": "material",
    "code": "NC-150",
    "name": "Nose Cone",
    "rocket_parts": []
  },
  {
    "id": 9,
    "type": "material",
    "code": "NC-600",
    "name": "Navigation Computer",
    "rocket_parts": []
  }
]

Count materials

To count the number of material records:

Count materials
GET http://127.0.0.1:8001/materials/count HTTP/1.1
Response
16

Update material name

This example updates the name of a material by calling its custom endpoint.

Update a specific material's name
POST http://127.0.0.1:8001/materials/1/update-name?name=Base%20Engine HTTP/1.1
Response
"Material name updated to 'Base Engine'."

Playing with rockets

Add some rockets

You can create rockets without specifying any parts. For instance:

Create some rockets without parts
POST http://127.0.0.1:8001/rockets HTTP/1.1
content-type: application/json

{
  "payload": [
    {
      "name": "Saturn V",
      "code": "SAT-V"
    },
    {
      "name": "Falcon 9",
      "code": "FAL-9"
    },
    {
      "name": "Space Launch System",
      "code": "SLS-1"
    }
  ]
}
Response
[
  {
    "id": 1,
    "type": "rocket",
    "code": "SAT-V",
    "name": "Saturn V",
    "parts": []
  },
  {
    "id": 2,
    "type": "rocket",
    "code": "FAL-9",
    "name": "Falcon 9",
    "parts": []
  },
  {
    "id": 3,
    "type": "rocket",
    "code": "SLS-1",
    "name": "Space Launch System",
    "parts": []
  }
]

Alternatively, rockets can be created with associated parts. In this example, parts are defined by linking to existing materials.

Create rockets with associated materials
POST http://127.0.0.1:8001/rockets HTTP/1.1
content-type: application/json

{
  "payload": [
    {
      "name": "Atlas V",
      "code": "ATL-6",
      "parts": [
        {
          "material": 3,
          "quantity": 1
        },
        ...
      ]
    },
    {
      "name": "Ariane 6",
      "code": "ARI-6",
      "parts": [
        {
          "material": {
            "name": "Attitude Control System",
            "code": "AC-650"
          },
          "quantity": 2
        },
        ...
      ]
    }
  ]
}
Request
POST http://127.0.0.1:8001/rockets HTTP/1.1
content-type: application/json

{
  "payload": [
    {
      "name": "Atlas V",
      "code": "ATL-6",
      "parts": [
        {
          "material": 3,
          "quantity": 1
        },
        {
          "material": 4,
          "quantity": 1
        },
        {
          "material": 7,
          "quantity": 2
        },
        {
          "material": 9,
          "quantity": 5
        },
        {
          "material": 10,
          "quantity": 4
        },
        {
          "material": 11,
          "quantity": 1
        }
      ]
    },
    {
      "name": "Ariane 6",
      "code": "ARI-6",
      "parts": [
        {
          "material": {
            "name": "Attitude Control System",
            "code": "AC-650"
          },
          "quantity": 2
        },
        {
          "material": {
            "name": "Telemetry System",
            "code": "TS-750"
          },
          "quantity": 1
        },
        {
          "material": {
            "name": "Staging Mechanism",
            "code": "SM-850"
          },
          "quantity": 3
        },
        {
          "material": {
            "name": "Propellant Feed System",
            "code": "PF-950"
          },
          "quantity": 3
        }
      ]
    }
  ]
}
Response
[
  {
    "id": 4,
    "type": "rocket",
    "code": "ATL-6",
    "name": "Atlas V",
    "parts": [
      {
        "id": 1,
        "type": "rocket_part",
        "material": {
          "id": 3,
          "type": "material",
          "code": "FT-501",
          "name": "Secondary Fuel Tank"
        },
        "quantity": 1
      },
      {
        "id": 2,
        "type": "rocket_part",
        "material": {
          "id": 4,
          "type": "material",
          "code": "GS-200",
          "name": "Guidance System"
        },
        "quantity": 1
      },
      {
        "id": 3,
        "type": "rocket_part",
        "material": {
          "id": 7,
          "type": "material",
          "code": "NC-150",
          "name": "Nose Cone"
        },
        "quantity": 2
      },
      {
        "id": 4,
        "type": "rocket_part",
        "material": {
          "id": 9,
          "type": "material",
          "code": "NC-600",
          "name": "Navigation Computer"
        },
        "quantity": 5
      },
      {
        "id": 5,
        "type": "rocket_part",
        "material": {
          "id": 10,
          "type": "material",
          "code": "CS-700",
          "name": "Communication System"
        },
        "quantity": 4
      },
      {
        "id": 6,
        "type": "rocket_part",
        "material": {
          "id": 11,
          "type": "material",
          "code": "TP-800",
          "name": "Thermal Protection System"
        },
        "quantity": 1
      }
    ]
  },
  {
    "id": 5,
    "type": "rocket",
    "code": "ARI-6",
    "name": "Ariane 6",
    "parts": [
      {
        "id": 7,
        "type": "rocket_part",
        "material": {
          "id": 17,
          "type": "material",
          "code": "AC-650",
          "name": "Attitude Control System"
        },
        "quantity": 2
      },
      {
        "id": 8,
        "type": "rocket_part",
        "material": {
          "id": 18,
          "type": "material",
          "code": "TS-750",
          "name": "Telemetry System"
        },
        "quantity": 1
      },
      {
        "id": 9,
        "type": "rocket_part",
        "material": {
          "id": 19,
          "type": "material",
          "code": "SM-850",
          "name": "Staging Mechanism"
        },
        "quantity": 3
      },
      {
        "id": 10,
        "type": "rocket_part",
        "material": {
          "id": 20,
          "type": "material",
          "code": "PF-950",
          "name": "Propellant Feed System"
        },
        "quantity": 3
      }
    ]
  }
]

Fetch rockets

Retrieve the list of rockets with or without their parts:

Fetch all rockets
GET http://127.0.0.1:8001/rockets HTTP/1.1
Response
[
  {
    "id": 1,
    "type": "rocket",
    "code": "SAT-V",
    "name": "Saturn V",
    "parts": []
  },
  {
    "id": 2,
    "type": "rocket",
    "code": "FAL-9",
    "name": "Falcon 9",
    "parts": []
  },
  {
    "id": 3,
    "type": "rocket",
    "code": "SLS-1",
    "name": "Space Launch System",
    "parts": []
  },
  {
    "id": 4,
    "type": "rocket",
    "code": "ATL-6",
    "name": "Atlas V",
    "parts": [
      {
        "id": 1,
        "type": "rocket_part",
        "material": {
          "id": 3,
          "type": "material",
          "code": "FT-501",
          "name": "Secondary Fuel Tank"
        },
        "quantity": 1
      },
      {
        "id": 2,
        "type": "rocket_part",
        "material": {
          "id": 4,
          "type": "material",
          "code": "GS-200",
          "name": "Guidance System"
        },
        "quantity": 1
      },
      {
        "id": 6,
        "type": "rocket_part",
        "material": {
          "id": 11,
          "type": "material",
          "code": "TP-800",
          "name": "Thermal Protection System"
        },
        "quantity": 1
      },
      {
        "id": 3,
        "type": "rocket_part",
        "material": {
          "id": 7,
          "type": "material",
          "code": "NC-150",
          "name": "Nose Cone"
        },
        "quantity": 2
      },
      {
        "id": 5,
        "type": "rocket_part",
        "material": {
          "id": 10,
          "type": "material",
          "code": "CS-700",
          "name": "Communication System"
        },
        "quantity": 4
      },
      {
        "id": 4,
        "type": "rocket_part",
        "material": {
          "id": 9,
          "type": "material",
          "code": "NC-600",
          "name": "Navigation Computer"
        },
        "quantity": 5
      }
    ]
  },
  {
    "id": 5,
    "type": "rocket",
    "code": "ARI-6",
    "name": "Ariane 6",
    "parts": [
      {
        "id": 8,
        "type": "rocket_part",
        "material": {
          "id": 18,
          "type": "material",
          "code": "TS-750",
          "name": "Telemetry System"
        },
        "quantity": 1
      },
      {
        "id": 7,
        "type": "rocket_part",
        "material": {
          "id": 17,
          "type": "material",
          "code": "AC-650",
          "name": "Attitude Control System"
        },
        "quantity": 2
      },
      {
        "id": 9,
        "type": "rocket_part",
        "material": {
          "id": 19,
          "type": "material",
          "code": "SM-850",
          "name": "Staging Mechanism"
        },
        "quantity": 3
      },
      {
        "id": 10,
        "type": "rocket_part",
        "material": {
          "id": 20,
          "type": "material",
          "code": "PF-950",
          "name": "Propellant Feed System"
        },
        "quantity": 3
      }
    ]
  }
]

Update rocket parts

You can update rocket parts by filtering the rocket(s) to update. For example, this call updates a rocket’s parts based on a code filter:

Update rocket materials using filter
PATCH http://127.0.0.1:8001/rockets?.code=like~*ARI* HTTP/1.1
content-type: application/json

{
  "payload": {
    "parts": [
      {
        "material": 1,
        "quantity": 2
      },
      {
        "material": 4,
        "quantity": 1
      }
    ]
  }
}
Response
{
  "id": 1,
  "type": "rocket",
  "code": "SAT-V",
  "name": "Saturn IB",
  "parts": [
    {
      "id": 11,
      "type": "rocket_part",
      "material": {
        "id": 1,
        "type": "material",
        "code": "ME-1000",
        "name": "Main Engine"
      },
      "quantity": 2
    },
    {
      "id": 12,
      "type": "rocket_part",
      "material": {
        "id": 4,
        "type": "material",
        "code": "GS-200",
        "name": "Guidance System"
      },
      "quantity": 1
    }
  ]
}

Fetch rocket parts

To retrieve the parts of a specific rocket:

Retrieve the updated rocket parts
GET http://127.0.0.1:8001/rockets/1/parts HTTP/1.1
Response
[
  {
    "id": 11,
    "type": "rocket_part",
    "material": {
      "id": 1,
      "type": "material",
      "code": "ME-1000",
      "name": "Main Engine"
    },
    "quantity": 2
  },
  {
    "id": 12,
    "type": "rocket_part",
    "material": {
      "id": 4,
      "type": "material",
      "code": "GS-200",
      "name": "Guidance System"
    },
    "quantity": 1
  }
]

Filter rocket parts

Finally, you can filter rocket parts by the material's properties. For example, to get parts where the material code matches a given pattern:

Fetch rocket parts that match specific conditions
GET http://127.0.0.1:8001/rockets/4/parts?.material.code=like~NC* HTTP/1.1
content-type: application/json

{
  "include": {
    "__all__": {
      "id": true,
      "material": {
        "code": true,
        "name": true
      },
      "quantity": true
    }
  }
}
Response
[
  {
    "id": 3,
    "material": {
      "code": "NC-150",
      "name": "Nose Cone"
    },
    "quantity": 2
  },
  {
    "id": 4,
    "material": {
      "code": "NC-600",
      "name": "Navigation Computer"
    },
    "quantity": 5
  }
]