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
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
- The
rocket_parts
field establishes a reverse relationship toRocketPart
, enabling bidirectional navigation between materials and their usage in rockets. - The
update_name
method is a custom route defined at the resource instance level. It updates thename
attribute of theMaterial
instance and returns a message confirming the update. It can be accessed at the/materials/{_material_key}/update-name
endpoint. - The
count
method is a custom route defined at the resource class level. It queries the database to count the number ofMaterial
instances and returns the result. It can be accessed at the/materials/count
endpoint. - The
parts
field now referencesRocketPart
instead ofMaterial
directly, allowing for additional attributes on the relationship. - A composite index ensures uniqueness of material-rocket combinations, preventing duplicate entries.
- 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.
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:
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:
GET http://127.0.0.1:8001/materials?.code=like~*NC*&.id=gt~2 HTTP/1.1
Count materials
To count the number of material records:
Update material name
This example updates the name of a material by calling its custom endpoint.
POST http://127.0.0.1:8001/materials/1/update-name?name=Base%20Engine HTTP/1.1
Playing with rockets
Add some rockets
You can create rockets without specifying any parts. For instance:
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"
}
]
}
Alternatively, rockets can be created with associated parts. In this example, parts are defined by linking to existing 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:
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:
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:
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: