Claude-skill-registry add-resource-events
Add real-time event emission to a resource service. Use when adding SSE/real-time capabilities to a resource. Triggers on "add events", "real-time events", "SSE events".
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/add-resource-events" ~/.claude/skills/majiayu000-claude-skill-registry-add-resource-events && rm -rf "$T"
manifest:
skills/data/add-resource-events/SKILL.mdsource content
Add Resource Events
Enables real-time event emission for a resource service via Server-Sent Events (SSE).
Quick Reference
Event Infrastructure:
- Singletonsrc/events/event-emitter.tsAppEventEmitter
-src/events/base.service.ts
withBaseServiceemitEvent()
- Event type definitionssrc/schemas/event.schema.ts
- SSE endpointsrc/routes/events.router.ts
Event Pattern:
{serviceName}:{action} (e.g., notes:created, notes:updated)
Prerequisites
To add events to a resource:
- Service must extend
(seeBaseService
)create-resource-service - Authorization methods for events in
AuthorizationService
How It Works
1. BaseService Provides emitEvent()
// src/events/base.service.ts export abstract class BaseService { constructor(protected serviceName: string) {} protected emitEvent<T>( action: ServiceEventType["action"], data: T, options?: { id?: string; user?: { userId: string; [key: string]: unknown }; }, ) { appEvents.emitServiceEvent(this.serviceName, { id: options?.id || uuidv4(), action, data, user: eventUser, timestamp: new Date(), resourceType: this.serviceName, }); } }
2. Resource Service Emits Events
export class NoteService extends BaseService { constructor(...) { super("notes"); // Service name for event routing } async create(data, user) { // ... create logic ... this.emitEvent("created", note, { id: note.id, user }); return note; } async update(id, data, user) { // ... update logic ... this.emitEvent("updated", updatedNote, { id: updatedNote.id, user }); return updatedNote; } async delete(id, user) { // ... delete logic ... this.emitEvent("deleted", note, { id: note.id, user }); return true; } }
3. Events Router Streams to Clients
// src/routes/events.router.ts appEvents.on("notes:created", eventHandler); appEvents.on("notes:updated", eventHandler); appEvents.on("notes:deleted", eventHandler);
Adding Events to a New Resource
Step 1: Extend BaseService
Your service must extend
BaseService:
import { BaseService } from "@/events/base.service"; export class {Entity}Service extends BaseService { constructor(...) { super("{entities}"); // Plural for event namespace } }
Step 2: Emit Events in CRUD Methods
Add
emitEvent() calls after successful operations:
async create(data, user) { const {entity} = await this.repository.create(data, user.userId); this.emitEvent("created", {entity}, { id: {entity}.id, user }); return {entity}; } async update(id, data, user) { const updated = await this.repository.update(id, data); if (!updated) return null; this.emitEvent("updated", updated, { id: updated.id, user }); return updated; } async delete(id, user) { const {entity} = await this.repository.findById(id); const deleted = await this.repository.remove(id); if (deleted) { this.emitEvent("deleted", {entity}, { id: {entity}.id, user }); } return deleted; }
Step 3: Add Event Authorization
In
src/services/authorization.service.ts:
async canReceive{Entity}Event( user: AuthenticatedUserContextType, {entity}Data: { createdBy: string; [key: string]: unknown }, ): Promise<boolean> { if (this.isAdmin(user)) return true; if ({entity}Data.createdBy === user.userId) return true; return false; }
Step 4: Update Events Router
In
src/routes/events.router.ts, add listeners:
// Listen to {entity} events appEvents.on("{entities}:created", eventHandler); appEvents.on("{entities}:updated", eventHandler); appEvents.on("{entities}:deleted", eventHandler);
Update the
shouldUserReceiveEvent function:
async function shouldUserReceiveEvent( event: ServiceEventType, user: AuthenticatedUserContextType, authorizationService: AuthorizationService, ): Promise<boolean> { switch (event.resourceType) { case "notes": // ... existing ... case "{entities}": if ( typeof event.data === "object" && event.data !== null && "createdBy" in event.data ) { return await authorizationService.canReceive{Entity}Event( user, event.data as { createdBy: string; [key: string]: unknown }, ); } return false; default: return false; } }
Step 5: Add Event Schema (Optional)
In
src/schemas/event.schema.ts:
export const {entity}EventSchema = serviceEventSchema.extend({ data: z.object({ id: z.string(), // ... entity-specific fields createdAt: z.date().optional(), updatedAt: z.date().optional(), }), }); export type {Entity}EventType = z.infer<typeof {entity}EventSchema>;
Event Flow
1. Client: POST /notes (create a note) 2. Controller: calls NoteService.create() 3. Service: creates note, calls this.emitEvent("created", note, ...) 4. BaseService: appEvents.emitServiceEvent("notes", { action: "created", ... }) 5. EventEmitter: emits "notes:created" event 6. Events Router: catches event, checks authorization 7. SSE Stream: sends to authorized clients 8. Client: receives { event: "notes:created", data: {...} }
Client-Side Usage
const eventSource = new EventSource("/events", { headers: { Authorization: `Bearer ${token}` }, }); eventSource.addEventListener("notes:created", (event) => { const data = JSON.parse(event.data); console.log("New note:", data); }); eventSource.addEventListener("notes:updated", (event) => { const data = JSON.parse(event.data); console.log("Updated note:", data); }); eventSource.addEventListener("notes:deleted", (event) => { const data = JSON.parse(event.data); console.log("Deleted note:", data); });
What NOT to Do
- Do NOT emit events before confirming operation succeeded
- Do NOT emit events for failed/unauthorized operations
- Do NOT skip authorization checks in events router
- Do NOT forget to clean up event listeners on disconnect
See Also
- Creating services with event emissioncreate-resource-service
- Events router examplecreate-routes
- Testing event emissiontest-resource-service