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 namedmodels.py(e.g.,common/myobject/models.py). Do NOT define models in__init__.py. - Cluster Prefix MUST Match EasySAM
prefix: The prefix passed toCluster('MyPrefix')inmodels.pymust start with the masterprefixdefined inresources.yaml(e.g., ifresources.yamlprefix ismy-app, cluster prefix must bemy-appormy-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-clientCommands in EasySAM: In EasySAM projects, Prismarine client code (prismarine_client.py) is automatically generated as an integrated step ofeasysam generate .andeasysam deploy .. Do NOT run separateprismarine generate-clientCLI commands. - Configure
access-modulefor Environment-Suffixed Tables: When deploying multi-environment stacks, tables are suffixed with stage/environment names. Configureaccess-module: common.dynamo_accessinresources.yamland create aDynamoAccessmodule exportingget_dynamo_access(). Consumer code MUST NEVER construct table names manually or call low-level_put_itemdirectly—always use generated model methods (Model.put(),Model.get()). - Use
DbConditionFailedfor Conditional Writes: PassConditionExpressiontoput()for atomic conditional writes. ImportDbConditionFailedfromprismarine.runtimeto catchConditionalCheckFailedException. - Match
modellingMode with Model Parent Class: Explicitly setmodelling: typed-dictormodelling: pydanticinresources.yaml. Models MUST inherit fromTypedDictwhen usingtyped-dictmode, orBaseModelwhen usingpydanticmode.
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': 'user@example.com', 'Name': 'Alice'})
# Conditional Write
try:
UserRecordModel.put(
{'Id': 'usr_123', 'Type': 'profile', 'Email': 'user@example.com', '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='user@example.com')
Reference Material
- easysam.md: EasySAM integration syntax (
resources.yaml, stream triggers, conditional tables, TTL). - model-definition.md: Complete
@c.model,@c.index,@c.exportAPI & Pydantic mode. - crud-api.md: Generated client methods (
get,put,update,save,delete,list,scan). - cli-usage.md: Standalone Prismarine CLI commands (non-EasySAM projects only).