Load this for decoupled/headless setups or when building a plugin/module.
Craft ships a first-party GraphQL API. Flow:
/api by default (configurable). POST GraphQL queries; auth via Authorization: Bearer <token>.Example query against entries (note the Craft 5 entry/entry-type model):
query Posts {
entries(section: "blog", limit: 10, orderBy: "postDate DESC") {
title
slug
... on blog_article_Entry {
postDate
featuredImage { url }
author { fullName }
}
}
}
The fragment type name follows {section}_{entryType}_Entry. Nested Matrix entries resolve as their own entry types under the Matrix field.
| Build a… | When |
|---|---|
| Module | Project-specific code, no distribution (modules/ in the app) |
| Plugin | Reusable/distributable via the Plugin Store (Composer package) |
Both extend Craft via Yii 2 components: services, controllers, element types, field types, widgets, behaviors, events.
my-plugin/
├── composer.json # type: craft-plugin, autoload PSR-4
├── src/
│ ├── Plugin.php # init(), registers services & event handlers
│ ├── services/ # business logic (injectable)
│ ├── controllers/ # CP + site request handlers
│ ├── elements/ # custom element types
│ ├── fields/ # custom field types
│ └── migrations/ # install + content migrations
└── README.md
Key registration patterns in Plugin::init():
// Register a service
$this->setComponents(['myService' => MyService::class]);
// Hook an event
Event::on(
Entries::class,
Entries::EVENT_AFTER_SAVE_ENTRY,
function (EntryEvent $e) { /* ... */ }
);
// Register a Twig extension
Craft::$app->view->registerTwigExtension(new MyTwigExtension());
Follow the official coding guidelines — namespacing, service-layer separation, and event-driven extension are expected idioms.
| Migration kind | Purpose |
|---|---|
| Install migration | Schema a plugin needs on install (migrations/Install.php) |
| Plugin migration | Schema changes between plugin versions |
| Content migration | Project data transformations (php craft migrate/create) — version-controlled, run via php craft up |
Always test migrations on a staging clone before production.
| Concern | Common choices |
|---|---|
| Frontend frameworks | Next.js, Nuxt, Astro, Gatsby via GraphQL / Element API |
| Hosting | Servd, Fortrabbit, Laravel Forge; DDEV for local |
| Assets | AWS S3, Google Cloud Storage, Imgix transforms |
| Search | Algolia, Elasticsearch via plugins |
| Commerce | Craft Commerce |
| Caching | Blitz (static pages), Redis, Cloudflare |