Event-driven functions with event-driven memory
Events trigger, memory persists
The Problem
Inngest is event-driven done right. Events trigger functions. Functions compose into workflows. Steps provide durability. Everything scales automatically, serverlessly. The developer experience is exceptional—define your function, subscribe to events, deploy, done.
But events are ephemeral by design.
Your user.signup event fires. A function processes it beautifully—welcome email, database update, analytics tracking. Then it's gone. When the next user.signup fires, the function has no memory of previous signups. It can't know that this is the 47th user from the same company domain, or that signups with certain email patterns tend to churn quickly.
You've built sophisticated event workflows. user.signup triggers onboarding. onboarding.complete triggers engagement. engagement.low triggers win-back. Each function in the chain is stateless. The win-back function doesn't know that this user had support issues during onboarding. It can't know that users from this segment respond better to certain messaging.
Event streams are full of patterns. Seasonal spikes. User behavior clusters. Failure correlations. Your functions process these events, but they can't learn from them. The institutional knowledge that would make your event handling smarter lives nowhere—not in your code, not in your database, nowhere accessible to the next invocation.
Event-driven architecture needs event-driven memory.
How Stompy Helps
Stompy adds persistent memory to Inngest's ephemeral events.
Each function step can query for relevant context before processing. What do we know about this user from previous events? What patterns have emerged for similar events? What worked and what didn't? Your event handlers become context-aware.
After processing, capture insights as durable context. Not just "user signed up" but "user from enterprise domain, signed up via referral, pattern suggests high engagement likelihood." This becomes searchable knowledge for all future events involving this user—or similar users.
Your event-driven functions gain learning capabilities: - **Cross-event intelligence**: Connect dots across user journeys that span dozens of events - **Pattern-aware processing**: Handle events differently based on accumulated context - **Workflow memory**: Functions in a chain share context about what's happened so far - **Failure prediction**: Recognize event patterns that historically lead to problems
Inngest handles event delivery and function durability. Stompy handles the memory layer.
Integration Walkthrough
Create memory utilities for Inngest
Build helper functions that integrate Stompy with Inngest's step model.
import { inngest } from "./client";// Stompy client for Inngest functionsconst STOMPY_URL = "https://mcp.stompy.ai/sse";const STOMPY_TOKEN = process.env.STOMPY_TOKEN!;async function searchContext(query: string, limit = 5): Promise<any[]> {const response = await fetch(STOMPY_URL, {method: "POST",headers: {"Authorization": `Bearer ${STOMPY_TOKEN}`,"Content-Type": "application/json"},body: JSON.stringify({tool: "context_search",params: { query, limit }})});const data = await response.json();return data.contexts || [];}async function saveContext(topic: string,content: string,tags: string[] = []): Promise<void> {await fetch(STOMPY_URL, {method: "POST",headers: {"Authorization": `Bearer ${STOMPY_TOKEN}`,"Content-Type": "application/json"},body: JSON.stringify({tool: "lock_context",params: {topic,content,tags: ["inngest", ...tags].join(",")}})});}
Build context-aware event handlers
Integrate memory into your Inngest functions for smarter event processing.
export const userSignupHandler = inngest.createFunction({ id: "user-signup-with-memory" },{ event: "app/user.signup" },async ({ event, step }) => {const { userId, email, referralSource } = event.data;const domain = email.split("@")[1];// Step 1: Get context about this user and similar patternsconst userContext = await step.run("get-user-context", async () => {const [userHistory, domainPatterns] = await Promise.all([searchContext(`user:${userId} events interactions`, 10),searchContext(`domain:${domain} signup patterns`, 5)]);return {previousInteractions: userHistory.length,knownPatterns: domainPatterns};});// Step 2: Adjust onboarding based on contextconst onboardingType = await step.run("determine-onboarding", async () => {if (userContext.previousInteractions > 0) {return "returning-user";}const hasEnterprisePattern = userContext.knownPatterns.some(p => p.content.includes("enterprise"));return hasEnterprisePattern ? "enterprise" : "standard";});// Step 3: Send appropriate welcomeawait step.run("send-welcome", async () => {await sendWelcomeEmail(email, onboardingType);});// Step 4: Save event context for future referenceawait step.run("save-signup-context", async () => {await saveContext(`signup_${userId}_${Date.now()}`,`User: ${userId}Email domain: ${domain}Referral: ${referralSource}Onboarding type: ${onboardingType}Previous interactions: ${userContext.previousInteractions}`,["signup", `domain:${domain}`, `onboarding:${onboardingType}`]);});return { userId, onboardingType };});
Build cross-event pattern detection
Create functions that analyze patterns across your event streams.
export const weeklyPatternAnalysis = inngest.createFunction({ id: "weekly-event-pattern-analysis" },{ cron: "0 9 * * 1" }, // Every Monday at 9amasync ({ step }) => {// Step 1: Analyze signup patternsconst signupPatterns = await step.run("analyze-signups", async () => {const signups = await searchContext("signup last 7 days", 100);const byDomain: Record<string, number> = {};const byOnboarding: Record<string, number> = {};for (const s of signups) {const domain = s.content.match(/Email domain: (\S+)/)?.[1];const onboarding = s.content.match(/Onboarding type: (\S+)/)?.[1];if (domain) byDomain[domain] = (byDomain[domain] || 0) + 1;if (onboarding) byOnboarding[onboarding] = (byOnboarding[onboarding] || 0) + 1;}return { total: signups.length, byDomain, byOnboarding };});// Step 2: Analyze engagement patternsconst engagementPatterns = await step.run("analyze-engagement", async () => {const engagements = await searchContext("engagement events last 7 days", 100);// Analyze engagement levels, feature usage, etc.return analyzeEngagement(engagements);});// Step 3: Save weekly insightsawait step.run("save-weekly-insights", async () => {await saveContext(`weekly_patterns_${new Date().toISOString().split('T')[0]}`,`Weekly Event AnalysisSignups: ${signupPatterns.total}Top domains: ${JSON.stringify(signupPatterns.byDomain, null, 2)}Onboarding distribution: ${JSON.stringify(signupPatterns.byOnboarding, null, 2)}Engagement Summary:${JSON.stringify(engagementPatterns, null, 2)}`,["weekly-report", "patterns", "analytics"]);});// Step 4: Notify team if significant patterns detectedif (signupPatterns.total > 1000 || engagementPatterns.churnRisk > 0.2) {await step.run("notify-team", async () => {await sendSlackNotification({channel: "#product-insights",message: `Significant patterns detected this week. Check Stompy for details.`});});}return { signupPatterns, engagementPatterns };});
What You Get
- Event handlers that understand user context from all previous interactions
- Cross-event pattern detection that reveals trends across your event streams
- Workflow functions that share context about user journeys
- Integration that preserves Inngest's step durability guarantees
- Serverless memory that scales with your serverless functions
Ready to give Inngest a memory?
Join the waitlist and be the first to know when Stompy is ready. Your Inngest projects will never forget again.