The CWA is in heavy development
The CWA is still in alpha and not ready for production - some code and implementations are likely to change. If you would like to try out the CWA, please enjoy what we have provided and feel free to provide feedback, or get involved on GitHub.
DraftAnnotations

Timestamped

Automatically record created and modified timestamps on any component or page data entity.

#[Silverback\Timestamped] automatically populates createdAt and modifiedAt on persist and flush. It's a lightweight annotation with no configuration required — add it and forget it.

Setup

use Silverback\ApiComponentsBundle\Annotation as Silverback;
use Silverback\ApiComponentsBundle\Entity\Core\AbstractComponent;
use Silverback\ApiComponentsBundle\Entity\Utility\TimestampedTrait;

#[Silverback\Timestamped]
#[ORM\Entity]
#[ApiResource(mercure: true)]
class Article extends AbstractComponent
{
    use TimestampedTrait;
}

What TimestampedTrait Adds

PropertyTypeBehaviour
createdAt?\DateTimeImmutableSet once on first persist; never updated after that
modifiedAt?\DateTimeUpdated on every Doctrine flush

Both are serialized and included in API responses automatically. They are read-only from the API — the bundle manages them, not the client.

Customising Field Names

The default names are createdAt and modifiedAt, matching TimestampedTrait. If those names conflict with your own fields, override them:

#[Silverback\Timestamped(createdAtField: 'publishedOn', modifiedAtField: 'lastEdited')]

When using custom names you must define the properties and getters/setters yourself rather than using TimestampedTrait:

#[Silverback\Timestamped(createdAtField: 'publishedOn', modifiedAtField: 'lastEdited')]
#[ORM\Entity]
#[ApiResource]
class Article extends AbstractComponent
{
    #[ORM\Column(nullable: true)]
    public ?\DateTimeImmutable $publishedOn = null;

    #[ORM\Column(nullable: true)]
    public ?\DateTime $lastEdited = null;
}

Combining with Publishable

Timestamps and publish workflow are commonly used together for blog-style content:

#[Silverback\Publishable]
#[Silverback\Timestamped]
#[ORM\Entity]
#[ApiResource(mercure: true)]
class Article extends AbstractComponent
{
    use PublishableTrait;
    use TimestampedTrait;

    public ?string $headline = null;
}

The createdAt and modifiedAt fields reflect the draft entity's lifecycle. The published twin gets its own timestamps set when it's first created.

Using Timestamps on the Front-End

Access them via resource.value?.data:

const resource = getResource()

const createdAt = computed(() =>
  resource.value?.data?.createdAt
    ? new Date(resource.value.data.createdAt).toLocaleDateString()
    : ''
)

Both properties are ISO 8601 strings in the API response. Use Intl.DateTimeFormat or a library like date-fns to format them for display.

Migration Notes

TimestampedTrait adds two nullable columns to your entity's table. The migration is straightforward — no foreign keys or join tables involved. createdAt is a datetime_immutable column; modifiedAt is a datetime column.