examples/new/.opencode/skills/axolotl-subscriptions/SKILL.md
Axolotl subscription handlers - async generators, yield patterns, PubSub integration, and federated subscription rules
npx skillsauth add aexol-studio/axolotl axolotl-subscriptionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
CRITICAL: Always use createSubscriptionHandler from @aexol/axolotl-core. Never use a raw AsyncGenerator.
import { createResolvers, createSubscriptionHandler } from '@aexol/axolotl-core';
import { setTimeout as setTimeout$ } from 'node:timers/promises';
export default createResolvers({
Subscription: {
countdown: createSubscriptionHandler(async function* (input, { from }) {
// input = [source, args, context] — same as regular resolvers
for (let i = from ?? 10; i >= 0; i--) {
await setTimeout$(1000);
yield i; // yield the value directly — NOT { countdown: i }
}
}),
},
});
Subscriptions require a Subscription type in the schema AND the subscription key in the schema block:
type Subscription {
countdown(from: Int): Int! @resolver
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
Yield the field value directly. GraphQL wraps it automatically:
// ✅ CORRECT
yield { type: 'CREATED', post: { _id: '123' } };
// → { "data": { "postUpdates": { "type": "CREATED", "post": {...} } } }
// ❌ WRONG — double-wraps the field name
yield { postUpdates: { type: 'CREATED', post: {...} } };
export default createResolvers({
Mutation: {
sendMessage: async ([, , ctx], { text }) => {
const message = { id: crypto.randomUUID(), text, timestamp: new Date().toISOString() };
await ctx.pubsub.publish('MESSAGE_ADDED', message);
return message;
},
},
Subscription: {
messageAdded: createSubscriptionHandler(async function* (input) {
const [, , ctx] = input;
const channel = ctx.pubsub.subscribe('MESSAGE_ADDED');
try {
for await (const message of channel) {
yield message;
}
} finally {
await channel.unsubscribe(); // cleanup on disconnect
}
}),
},
});
tools
Baseline architecture for Axolotl mobile starter (Expo Router + reusable blocks)
tools
Expo Router conventions for route groups, native headers, and starter navigation
development
i18n baseline and dev-translate setup for Expo mobile starter
development
Starter data layer pattern with React Query + Zeus for Expo app