Skip to content

Basic examples with services

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.

No specification

The following example demonstrates how to create a basic service without a resource specification. This is useful when you want to create a service that doesn't need any specific resources. Unlike services with specifications, basic services can also be attached directly to packages.

Implementation

Create service

Create a service without a resource specification
from plateforme import BaseService, route

class NoSpecService(BaseService):
    """A service without specification."""

    @route.post()
    async def hello(self) -> str:  # (1)!
        return 'Hello, world!'
  1. We create a simple POST endpoint that returns a string message.

Assign the service

Assign the service to a resource
from plateforme import BaseResource, ConfigDict

class Material(BaseResource):
    __config__ = ConfigDict(services=[NoSpecService])
Source code in src/examples/basic_services/example_no_spec.py
from pathlib import Path
from plateforme import Plateforme

dirname = Path(__file__).parent

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

# Example
from plateforme import BaseService, route

class NoSpecService(BaseService):
    """A service without specification."""

    @route.post()
    async def hello(self) -> str:  # (1)!
        return 'Hello, world!'

from plateforme import BaseResource, ConfigDict

class Material(BaseResource):
    __config__ = ConfigDict(services=[NoSpecService])

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

Playground

Call hello endpoint

We can directly call the service hello endpoint.

Call the service 'hello' endpoint
POST http://127.0.0.1:8001/materials/hello HTTP/1.1
Response
"Hello, world!"

With simple specification

The following example demonstrates how to create a basic service with a simple specification. This is useful when you want to create a service that can be attached to multiple resources. The specification is like a contract that the resources must fulfill to be able to use the service. In this example, we create a service that can be attached to "orderable" resources, i.e. resources that expose a price attribute.

Implementation

Create service

Create a service with a simple specification
from plateforme import BaseServiceWithSpec, BaseSpec
from plateforme.api import AsyncSessionDep, route
from plateforme.database import select

class OrderableSpec(BaseSpec):
    """An orderable specification."""
    price: float

class SimpleSpecService(BaseServiceWithSpec[OrderableSpec]):
    """A service with a simple specification."""

    @route.post()
    async def buy(
        self,
        session: AsyncSessionDep,
        payload: dict[str, int],
    ) -> float:
        ids = [int(k) for k in payload.keys()]
        query = select(self.resource).filter(self.resource.id.in_(ids))
        buffer = await session.execute(query)
        result = buffer.scalars().all()

        if len(result) != len(payload):
            raise ValueError('Invalid payload')

        total = 0
        for item in result:
            total += item.price * payload[str(item.id)]

        return total

Assign the service

Assign the service to some resources
from plateforme import ConfigDict, CRUDResource, Field

class Material(CRUDResource):
    __config__ = ConfigDict(services=[..., SimpleSpecService])

    code: str = Field(unique=True)
    name: str
    price: float

class Rocket(CRUDResource):
    __config__ = ConfigDict(services=[..., SimpleSpecService])

    code: str = Field(unique=True)
    name: str
    price: float
Source code in src/examples/basic_services/example_simple_spec.py
from pathlib import Path
from plateforme import Plateforme

dirname = Path(__file__).parent

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

# Example
from plateforme import BaseServiceWithSpec, BaseSpec
from plateforme.api import AsyncSessionDep, route
from plateforme.database import select

class OrderableSpec(BaseSpec):
    """An orderable specification."""
    price: float

class SimpleSpecService(BaseServiceWithSpec[OrderableSpec]):
    """A service with a simple specification."""

    @route.post()
    async def buy(
        self,
        session: AsyncSessionDep,
        payload: dict[str, int],
    ) -> float:
        ids = [int(k) for k in payload.keys()]
        query = select(self.resource).filter(self.resource.id.in_(ids))
        buffer = await session.execute(query)
        result = buffer.scalars().all()

        if len(result) != len(payload):
            raise ValueError('Invalid payload')

        total = 0
        for item in result:
            total += item.price * payload[str(item.id)]

        return total

from plateforme import ConfigDict, CRUDResource, Field

class Material(CRUDResource):
    __config__ = ConfigDict(services=[..., SimpleSpecService])

    code: str = Field(unique=True)
    name: str
    price: float

class Rocket(CRUDResource):
    __config__ = ConfigDict(services=[..., SimpleSpecService])

    code: str = Field(unique=True)
    name: str
    price: float

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

Playground

Add some resources

Let's create some materials and rockets.

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

{
  "payload": [
    {
      "name": "Main Engine",
      "code": "ME-1000",
      "price": 1500000.00
    },
    {
      "name": "Primary Fuel Tank",
      "code": "FT-500",
      "price": 145000.00
    },
    {
      "name": "Guidance System",
      "code": "GS-200",
      "price": 120000.00
    }
  ]
}
Response
[
  {
    "id": 1,
    "type": "material",
    "code": "ME-1000",
    "name": "Main Engine",
    "price": 1500000.0
  },
  {
    "id": 2,
    "type": "material",
    "code": "FT-500",
    "name": "Primary Fuel Tank",
    "price": 145000.0
  },
  {
    "id": 3,
    "type": "material",
    "code": "GS-200",
    "name": "Guidance System",
    "price": 120000.0
  }
]
Add some rockets
POST http://127.0.0.1:8001/rockets HTTP/1.1
content-type: application/json

{
  "payload": [
    {
      "name": "Saturn V",
      "code": "SAT-V",
      "price": 240000000.00
    },
    {
      "name": "Falcon 9",
      "code": "FAL-9",
      "price": 62000000.00
    },
    {
      "name": "Space Launch System",
      "code": "SLS-1",
      "price": 500000000.00
    }
  ]
}
Response
[
  {
    "id": 1,
    "type": "rocket",
    "code": "SAT-V",
    "name": "Saturn V",
    "price": 240000000.0
  },
  {
    "id": 2,
    "type": "rocket",
    "code": "FAL-9",
    "name": "Falcon 9",
    "price": 62000000.0
  },
  {
    "id": 3,
    "type": "rocket",
    "code": "SLS-1",
    "name": "Space Launch System",
    "price": 500000000.0
  }
]

Call buy endpoint

We can now call the service buy endpoint on materials and rockets.

Call the service 'buy' endpoint
POST http://127.0.0.1:8001/materials/buy HTTP/1.1
content-type: application/json

{
  "1": 2,
  "2": 1,
  "3": 1
}
Response
3265000.0
Call the service 'buy' endpoint
POST http://127.0.0.1:8001/rockets/buy HTTP/1.1
content-type: application/json

{
  "1": 2,
  "2": 1,
  "3": 1
}
Response
1042000000.0

With selection specification

The following example demonstrates how to create a basic service with a selection specification. This is useful when you want to create endpoints either at the resource collection level or at the resource instance level. The selection parameter will be filled at runtime with the selected resources inferred from the URL. In this example, we create a service that can be attached to both materials and rockets.

Implementation

Create service

Create a service with a selection specification
from plateforme import BaseServiceWithSpec, BaseSpec, Key, KeyList
from plateforme.api import AsyncSessionDep, Selection, route

class DescriptionSpec(BaseSpec):
    """A description specification."""
    code: str
    name: str

class SelectionSpecService(BaseServiceWithSpec[DescriptionSpec]):
    """A service with a selection specification."""

    @route.get(path='/describe')
    async def describe_one(
        self,
        session: AsyncSessionDep,
        selection: Key[DescriptionSpec] = Selection(),
    ) -> str:
        result = await selection.resolve(session)
        return f"{result.name} ({result.code})"

    @route.get(path='/describe')
    async def describe_many(
        self,
        session: AsyncSessionDep,
        selection: KeyList[DescriptionSpec] = Selection(),
    ) -> list[str]:
        result = await selection.resolve(session)
        descriptions = []
        for item in result:
            descriptions.append(f"{item.name} ({item.code})")
        return descriptions

Assign the service

Assign the service to some resources
from plateforme import ConfigDict, CRUDResource, Field

class Material(CRUDResource):
    __config__ = ConfigDict(services=[..., SelectionSpecService])

    code: str = Field(unique=True)
    name: str

class Rocket(CRUDResource):
    __config__ = ConfigDict(services=[..., SelectionSpecService])

    code: str = Field(unique=True)
    name: str
Source code in src/examples/basic_services/example_selection_spec.py
from pathlib import Path
from plateforme import Plateforme

dirname = Path(__file__).parent

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

# Example
from plateforme import BaseServiceWithSpec, BaseSpec, Key, KeyList
from plateforme.api import AsyncSessionDep, Selection, route

class DescriptionSpec(BaseSpec):
    """A description specification."""
    code: str
    name: str

class SelectionSpecService(BaseServiceWithSpec[DescriptionSpec]):
    """A service with a selection specification."""

    @route.get(path='/describe')
    async def describe_one(
        self,
        session: AsyncSessionDep,
        selection: Key[DescriptionSpec] = Selection(),
    ) -> str:
        result = await selection.resolve(session)
        return f"{result.name} ({result.code})"

    @route.get(path='/describe')
    async def describe_many(
        self,
        session: AsyncSessionDep,
        selection: KeyList[DescriptionSpec] = Selection(),
    ) -> list[str]:
        result = await selection.resolve(session)
        descriptions = []
        for item in result:
            descriptions.append(f"{item.name} ({item.code})")
        return descriptions

from plateforme import ConfigDict, CRUDResource, Field

class Material(CRUDResource):
    __config__ = ConfigDict(services=[..., SelectionSpecService])

    code: str = Field(unique=True)
    name: str

class Rocket(CRUDResource):
    __config__ = ConfigDict(services=[..., SelectionSpecService])

    code: str = Field(unique=True)
    name: str

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

Playground

Add some resources

Let's create some materials and rockets.

Add 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"
    },
    {
      "name": "Guidance System",
      "code": "GS-200"
    }
  ]
}
Response
[
  {
    "id": 1,
    "type": "material",
    "code": "ME-1000",
    "name": "Main Engine"
  },
  {
    "id": 2,
    "type": "material",
    "code": "FT-500",
    "name": "Primary Fuel Tank"
  },
  {
    "id": 3,
    "type": "material",
    "code": "GS-200",
    "name": "Guidance System"
  }
]
Add some rockets
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"
  },
  {
    "id": 2,
    "type": "rocket",
    "code": "FAL-9",
    "name": "Falcon 9"
  },
  {
    "id": 3,
    "type": "rocket",
    "code": "SLS-1",
    "name": "Space Launch System"
  }
]

Call describe-many endpoint

We can now call the service describe endpoint on materials and rockets.

Call the service 'describe' endpoint on materials
GET http://127.0.0.1:8001/materials/describe HTTP/1.1
Response
[
  "Main Engine (ME-1000)",
  "Primary Fuel Tank (FT-500)",
  "Guidance System (GS-200)"
]
Call the service 'describe' endpoint on rockets
GET http://127.0.0.1:8001/rockets/describe HTTP/1.1
Response
[
  "Saturn V (SAT-V)",
  "Falcon 9 (FAL-9)",
  "Space Launch System (SLS-1)"
]

Call describe-one endpoint

Or, alternatively, we can call the service describe endpoint on a specific material or rocket.

Call the service 'describe' endpoint on a specific material
GET http://127.0.0.1:8001/materials/2/describe HTTP/1.1
Response
"Primary Fuel Tank (FT-500)"
Call the service 'describe' endpoint on a specific rocket
GET http://127.0.0.1:8001/rockets/1/describe HTTP/1.1
Response
"Saturn V (SAT-V)"