Prismarine Skill
This skill provides guidelines and patterns for using Prismarine, a model-driven DynamoDB ORM for EasySAM and Python applications.
Core Directives & Rules
- Models MUST Be in
models.py:
Define Prismarine models inside a file explicitly named models.py (e.g., common/myobject/models.py). Do NOT define models in init.py.
- Cluster Prefix MUST Match EasySAM
prefix:
The prefix passed to Cluster('MyPrefix') in models.py must start with the master prefix defined in resources.yaml (e.g., if resources.yaml prefix is my-app, cluster prefix must be my-app or my-app-users).
- Do NOT Manually Define DynamoDB Tables in
easysam.yaml:
EasySAM automatically inspects Prismarine models during preprocessing to create and register DynamoDB tables, indexes, TTL, and stream triggers in CloudFormation.
- Order Decorators Correctly:
Place @c.index(...) decorators ABOVE @c.model(...) decorators.
- Do NOT Run Standalone
prismarine generate-client Commands in EasySAM:
In EasySAM projects, Prismarine client code (prismarine_client.py) is automatically generated as an integrated step of easysam generate . and easysam deploy .. Do NOT run separate prismarine generate-client CLI commands.
- Configure
access-module for Environment-Suffixed Tables:
When deploying multi-environment stacks, tables are suffixed with stage/environment names. Configure access-module: common.dynamoaccess in resources.yaml and create a DynamoAccess module exporting getdynamoaccess(). Consumer code MUST NEVER construct table names manually or call low-level put_item directly—always use generated model methods (Model.put(), Model.get()).
- Use
DbConditionFailed for Conditional Writes:
Pass ConditionExpression to put() for atomic conditional writes. Import DbConditionFailed from prismarine.runtime to catch ConditionalCheckFailedException.
- Match
modelling Mode with Model Parent Class:
Explicitly set modelling: typed-dict or modelling: pydantic in resources.yaml. Models MUST inherit from TypedDict when using typed-dict mode, or BaseModel when using pydantic mode.
Standard Project Layout
When integrating Prismarine with EasySAM, use the following package structure:
my-project/
├── resources.yaml # Root config with prismarine: section
├── common/
│ ├── dynamo_access.py # Access module for environment-suffixed tables
│ └── myobject/
│ ├── models.py # Prismarine Cluster & model definitions
│ └── prismarine_client.py # Auto-generated client code (created by easysam generate/deploy)
├── backend/
│ └── function/
│ └── my-function/
│ ├── easysam.yaml # Lambda function definition
│ └── index.py # Handler importing common.myobject.prismarine_client
Data Access Workflow
1. Define Model Cluster (common/myobject/models.py)
from typing import TypedDict, NotRequired
from prismarine.runtime import Cluster
c = Cluster('MyApp')
@c.index(index='by-email', PK='Email') # Must be ABOVE @c.model
@c.model(PK='Id', SK='Type', ttl='ExpireAt', trigger='itemlogger')
class UserRecord(TypedDict):
Id: str
Type: str
Email: str
Name: str
ExpireAt: NotRequired[int]
2. Configure resources.yaml
prefix: my-app
python: 3.12
prismarine:
default-base: common
access-module: common.dynamo_access
modelling: typed-dict # or: pydantic
tables:
- package: myobject
trigger: true # Preserve model-defined triggers
3. Generate & Deploy (Automatic)
Simply run standard EasySAM commands. EasySAM handles Prismarine table preprocessing and client generation internally:
# Preprocesses models, validates schema, generates template, and writes prismarine_client.py
uv run easysam --environment dev generate .
# Builds and deploys application and Prismarine models to AWS
uv run easysam --environment dev --aws-profile <profile> deploy .
(Note: Standalone prismarine generate-client CLI usage is only for non-EasySAM standalone projects).
4. Execute CRUD Operations
from common.myobject.prismarine_client import UserRecordModel
from prismarine.runtime import DbNotFound, DbConditionFailed
# Create / Replace
UserRecordModel.put({'Id': 'usr_123', 'Type': 'profile', 'Email': '[email protected]', 'Name': 'Alice'})
# Conditional Write
try:
UserRecordModel.put(
{'Id': 'usr_123', 'Type': 'profile', 'Email': '[email protected]', 'Name': 'Alice'},
ConditionExpression='attribute_not_exists(Id)',
)
except DbConditionFailed:
pass # Item already exists
# Get
try:
user = UserRecordModel.get(Id='usr_123', Type='profile')
except DbNotFound:
user = None
# Query Secondary Index
users = UserRecordModel.ByEmail.list(Email='[email protected]')
Reference Material
- [easysam.md](references/easysam.md): EasySAM integration syntax (
resources.yaml, stream triggers, conditional tables, TTL).
- [model-definition.md](references/model-definition.md): Complete
@c.model, @c.index, @c.export API & Pydantic mode.
- [crud-api.md](references/crud-api.md): Generated client methods (
get, put, update, save, delete, list, scan).
- [cli-usage.md](references/cli-usage.md): Standalone Prismarine CLI commands (non-EasySAM projects only).