CtrlK
BlogDocsLog inGet started
Tessl Logo

strapi-cms

Builds Strapi content types, extends controllers and services, implements lifecycle hooks, and configures REST/GraphQL APIs. Use when creating content types, writing custom controllers, developing Strapi plugins, or querying the API.

100

Quality

100%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

SKILL.md
Quality
Evals
Security
<!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .opencastle/ directory instead. -->

Strapi CMS

Generic Strapi CMS development methodology. For project-specific configuration, content types, and deployment details, see cms-config.md.

Critical Development Rules

  1. Use Content-Type Builder — define content types through the admin panel or content-types directory
  2. REST API by default — Strapi exposes REST endpoints automatically; enable GraphQL plugin if needed
  3. Extend controllers — implement src/api/<type>/controllers/<type>.js with wrapper logic that calls services
  4. Implement services — place business logic in src/api/<type>/services/ and keep controllers thin
  5. Use lifecycle hooks — add beforeCreate/afterUpdate handlers in src/api/<type>/lifecycles.js for side effects
  6. Configure permissions per role — set public/authenticated access in Users & Permissions; keep environment configs under config/env/<env>/

API Patterns

REST API (Strapi-specific)

  • Use populate to include relations
  • Use filters with operators ($eq, $contains, $in, etc.)
  • Pagination via pagination[page] and pagination[pageSize]
  • Use fields to select attributes

Quick REST examples

GET with populate and filters (client-side fetch):

// GET /api/articles?populate=author,categories&filters[status][$eq]=published&pagination[page]=1&pagination[pageSize]=10
const res = await fetch('https://cms.example.com/api/articles?populate=author,categories&filters[status][$eq]=published');
const json = await res.json();
console.log(json.data[0].attributes.title);

Custom controller extension (server):

// src/api/article/controllers/article.js
const { createCoreController } = require('@strapi/strapi').factories;

module.exports = createCoreController('api::article.article', ({ strapi }) => ({
	async find(ctx) {
		// call default then modify response
		const res = await super.find(ctx);
		// add extra field
		res.meta.custom = { processedAt: new Date().toISOString() };
		return res;
	},
}));

Lifecycle hook example:

// src/api/article/content-types/article/lifecycles.js
module.exports = {
	async beforeCreate(event) {
		const { data } = event.params;
		if (data.title) data.slug = slugify(data.title);
	},
};

Quick workflow: create content type + custom controller + service

  1. Create content type using Content-Type Builder or src/api/<type>/content-types/schema.json → commit schema.
  2. Scaffold controller and service files under src/api/<type>/controllers/ and src/api/<type>/services/ with thin controller calling service functions.
  3. Add lifecycle hooks (optional) in content-types/<type>/lifecycles.js for side effects.
  4. Run local Strapi (yarn develop) → check admin UI for new content type.
    • Validation: create a test entry in admin UI and confirm via GET /api/<type>?pagination[page]=1 that fields exist.
    • If fail: check server logs, run yarn build to surface schema errors, verify schema.json validity.
  5. Add permissions (Users & Permissions) for public/authenticated roles if API access required.
  6. Add automated API test: fetch('/api/<type>?populate=*') in test suite to validate relations populate.

For more reference patterns and larger examples, see REFERENCE.md.

Repository
monkilabs/opencastle
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.