Learn how to configure and monitor content types for real-time events.
The contentTypes configuration controls which content types emit events to connected clients. It supports two formats: string and object.
Use the content type UID directly to enable all events:
module.exports = ({ env }) => ({
io: {
enabled: true,
config: {
contentTypes: [
'api::article.article',
'api::comment.comment',
'api::product.product'
]
}
}
});This automatically enables:
- ✅ Create events (
article:create) - ✅ Update events (
article:update) - ✅ Delete events (
article:delete)
For fine-grained control, use the object format to specify exactly which actions should emit events:
module.exports = ({ env }) => ({
io: {
enabled: true,
config: {
contentTypes: [
// Only create events
{
uid: 'api::article.article',
actions: ['create']
},
// Create and update, no delete
{
uid: 'api::product.product',
actions: ['create', 'update']
},
// All events (same as string format)
{
uid: 'api::comment.comment',
actions: ['create', 'update', 'delete']
}
]
}
}
});| Action | Event Name | Description |
|---|---|---|
create |
{contentType}:create |
Fired when entry is created |
update |
{contentType}:update |
Fired when entry is updated |
delete |
{contentType}:delete |
Fired when entry is deleted |
Populate relations automatically in emitted events:
contentTypes: [
{
uid: 'api::article.article',
actions: ['create', 'update'],
populate: ['author', 'category', 'tags']
}
]Frontend receives:
socket.on('article:create', (article) => {
console.log(article.attributes.author); // ✅ Populated
console.log(article.attributes.category); // ✅ Populated
console.log(article.attributes.tags); // ✅ Populated
});Override default event naming:
contentTypes: [
{
uid: 'api::article.article',
actions: ['create'],
eventPrefix: 'new-article' // Emits as 'new-article:create'
}
]Frontend listens to:
socket.on('new-article:create', (data) => {
console.log('New article published!', data);
});// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
contentTypes: [
// Articles - all events with relations
{
uid: 'api::article.article',
actions: ['create', 'update', 'delete'],
populate: ['author', 'category', 'coverImage']
},
// Comments - only create
{
uid: 'api::comment.comment',
actions: ['create'],
populate: ['author']
},
// Categories - all events, no relations
'api::category.category'
]
}
}
};Frontend:
import { io } from 'socket.io-client';
const socket = io('http://localhost:1337');
// New articles
socket.on('article:create', (article) => {
addArticleToFeed(article);
showNotification(`New article: ${article.attributes.title}`);
});
// Article updates
socket.on('article:update', (article) => {
updateArticleInFeed(article);
});
// New comments
socket.on('comment:create', (comment) => {
addCommentToArticle(comment);
playNotificationSound();
});// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
contentTypes: [
// Products - create and update only
{
uid: 'api::product.product',
actions: ['create', 'update'],
populate: ['images', 'category', 'variants']
},
// Orders - all events
{
uid: 'api::order.order',
actions: ['create', 'update'],
populate: ['customer', 'items']
},
// Stock updates
{
uid: 'api::inventory.inventory',
actions: ['update']
}
]
}
}
};Frontend:
// Product page - update price/stock in real-time
socket.on('product:update', (product) => {
if (product.id === currentProductId) {
updatePrice(product.attributes.price);
updateStock(product.attributes.stock);
if (product.attributes.stock === 0) {
showOutOfStockBanner();
}
}
});
// Cart page - notify about stock changes
socket.on('inventory:update', (inventory) => {
const cartItem = findCartItem(inventory.productId);
if (cartItem && inventory.stock < cartItem.quantity) {
showLowStockWarning(inventory);
}
});// config/plugins.js
module.exports = {
io: {
enabled: true,
config: {
contentTypes: [
// Monitor all user activities
{
uid: 'plugin::users-permissions.user',
actions: ['create', 'update'],
populate: ['role']
},
// Track content creation
'api::article.article',
'api::comment.comment',
'api::media.media',
// Monitor orders
{
uid: 'api::order.order',
actions: ['create', 'update'],
populate: ['customer', 'items']
}
]
}
}
};Admin Frontend:
const socket = io('http://localhost:1337/admin', {
auth: { strategy: 'api-token', token: adminToken }
});
// Live activity feed
socket.on('article:create', (article) => {
addToActivityFeed('New article published', article);
});
socket.on('user:create', (user) => {
addToActivityFeed('New user registered', user);
updateUserCount();
});
socket.on('order:create', (order) => {
addToActivityFeed('New order received', order);
playNotificationSound();
updateRevenue(order.attributes.total);
});Only emit events for published content:
// src/api/article/content-types/article/lifecycles.js
module.exports = {
async afterCreate(event) {
const { result } = event;
// Only emit if published
if (result.publishedAt) {
await strapi.$io.emit({
event: 'create',
schema: 'api::article.article',
data: result
});
}
}
};Control which roles receive which events via Admin Panel:
Settings → Socket.IO → Role Permissions
-
Select Public role:
- Enable
article:create✅ - Disable
article:delete❌
- Enable
-
Select Authenticated role:
- Enable all article events ✅
Reduce payload size by excluding heavy fields:
// In Admin Panel Settings
events: {
excludeFields: ['content', 'largeData', 'internalNotes']
}Or programmatically:
// src/api/article/content-types/article/lifecycles.js
module.exports = {
async afterCreate(event) {
const { result } = event;
// Custom emit with limited fields
await strapi.$io.raw({
event: 'article:create',
data: {
id: result.id,
title: result.title,
excerpt: result.excerpt,
author: result.author.username
// Exclude heavy content field
}
});
}
};Prevent event flooding during imports:
const { debounce } = require('lodash');
const emitBulkUpdate = debounce((ids) => {
strapi.$io.raw({
event: 'articles:bulk-update',
data: { updatedIds: ids }
});
}, 1000);
// In bulk operation
bulkUpdate.forEach(async (article) => {
await strapi.entityService.update('api::article.article', article.id, {
data: article
});
emitBulkUpdate(article.id);
});// test/content-types.test.js
const { io } = require('socket.io-client');
describe('Content Type Events', () => {
let socket;
beforeAll(() => {
socket = io('http://localhost:1337');
});
afterAll(() => {
socket.disconnect();
});
test('should receive article:create event', (done) => {
socket.on('article:create', (article) => {
expect(article).toHaveProperty('id');
expect(article.attributes).toHaveProperty('title');
done();
});
// Create article via API
fetch('http://localhost:1337/api/articles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: {
title: 'Test Article',
content: 'Test content'
}
})
});
});
});Check content type is enabled:
const settings = await strapi.plugin('io').service('settings').getSettings();
console.log('Enabled content types:', settings.contentTypes);Check role permissions:
Navigate to Settings → Socket.IO → Role Permissions and verify the role has access.
Verify event name:
Events follow the pattern {singularName}:{action}. For api::article.article, events are:
article:createarticle:updatearticle:delete
- Plugin Configuration - Complete configuration reference
- Events Examples - Custom event handlers
- API Reference - Emit methods and options