MemberPulse
IntegrationsBackend

Convex Integration

Real-time backend platform for live data sync, serverless functions, and file storage

Overview

Convex is a real-time backend platform that powers MemberPulse's live features including real-time notifications, live dashboards, collaborative features, and instant data synchronization across all connected clients.

Real-time Sync

Instant data updates across all clients

Serverless Functions

Backend logic without infrastructure

File Storage

Integrated file storage with CDN


Why Convex?

FeatureBenefit
Real-time by DefaultAll queries automatically update when data changes
ACID TransactionsStrong consistency guarantees for data integrity
TypeScript NativeEnd-to-end type safety from database to UI
ServerlessNo infrastructure to manage, scales automatically
Built-in AuthIntegrates with existing auth providers

Prerequisites

Before configuring Convex:

  1. Create a Convex account at convex.dev
  2. Install the Convex CLI: npm install -g convex
  3. Initialize your project: npx convex dev

Configuration

Environment Variables

# Backend
CONVEX_DEPLOYMENT=prod:memberpulse-xxxxx
CONVEX_DEPLOY_KEY=prod:xxxxxxxxxxxxxxxxxxxx

# Frontend
NEXT_PUBLIC_CONVEX_URL=https://memberpulse-xxxxx.convex.cloud

Acceptance Criteria

Frontend
  • Environment Variables workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Environment Variables as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Platform Settings

Navigate to Admin Portal > Platform Settings > Integrations > Convex

SettingTypeRequiredDescription
Deployment URLURLYesConvex deployment URL
Deploy KeySecretYesProduction deploy key
Enable Real-timeToggleYesEnable real-time features
Sync IntervalNumberNoFallback polling interval (ms)

Acceptance Criteria

Frontend
  • Platform Settings workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Platform Settings as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Architecture

Data Flow

graph LR
    A[React Client] -->|Subscribe| B[Convex Cloud]
    B -->|Real-time Updates| A
    C[Symfony API] -->|Mutations| B
    B -->|Webhooks| C
    B -->|Store| D[(Convex DB)]
    B -->|Files| E[Convex Storage]

Acceptance Criteria

Frontend
  • Data Flow workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Data Flow as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Hybrid Architecture

MemberPulse uses a hybrid architecture:

Data TypeStorageReason
Core DataPostgreSQL (Symfony)GDPR compliance, complex queries
Real-time DataConvexLive updates, collaboration
FilesConvex StorageCDN delivery, easy uploads
SessionsConvexReal-time presence

Acceptance Criteria

Frontend
  • Hybrid Architecture workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Hybrid Architecture as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Real-time Features

Live Notifications

// convex/notifications.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";

export const list = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("notifications")
      .withIndex("by_user", (q) => q.eq("userId", args.userId))
      .filter((q) => q.eq(q.field("read"), false))
      .order("desc")
      .take(50);
  },
});

export const markAsRead = mutation({
  args: { notificationId: v.id("notifications") },
  handler: async (ctx, args) => {
    await ctx.db.patch(args.notificationId, { read: true });
  },
});
// React component
import { useQuery, useMutation } from "convex/react";
import { api } from "../convex/_generated/api";

function NotificationBell() {
  const notifications = useQuery(api.notifications.list, { 
    userId: currentUser.id 
  });
  const markAsRead = useMutation(api.notifications.markAsRead);

  return (
    <DropdownMenu>
      <Badge count={notifications?.length || 0}>
        <BellIcon />
      </Badge>
      <DropdownContent>
        {notifications?.map((n) => (
          <NotificationItem 
            key={n._id} 
            notification={n}
            onRead={() => markAsRead({ notificationId: n._id })}
          />
        ))}
      </DropdownContent>
    </DropdownMenu>
  );
}

Acceptance Criteria

Frontend
  • Live Notifications workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Live Notifications as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Live Dashboard Metrics

// convex/metrics.ts
export const dashboardStats = query({
  args: { organizationId: v.string() },
  handler: async (ctx, args) => {
    const [activeUsers, pendingApprovals, recentActivity] = await Promise.all([
      ctx.db
        .query("presence")
        .withIndex("by_org", (q) => q.eq("organizationId", args.organizationId))
        .filter((q) => q.gt(q.field("lastSeen"), Date.now() - 300000))
        .collect(),
      ctx.db
        .query("approvals")
        .withIndex("by_org_status", (q) => 
          q.eq("organizationId", args.organizationId).eq("status", "pending")
        )
        .collect(),
      ctx.db
        .query("activity")
        .withIndex("by_org", (q) => q.eq("organizationId", args.organizationId))
        .order("desc")
        .take(20),
    ]);

    return {
      activeUsersCount: activeUsers.length,
      pendingApprovalsCount: pendingApprovals.length,
      recentActivity,
    };
  },
});

Acceptance Criteria

Frontend
  • Live Dashboard Metrics workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Live Dashboard Metrics as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Real-time Presence

Show who's online and what they're viewing:

// convex/presence.ts
export const heartbeat = mutation({
  args: {
    userId: v.string(),
    organizationId: v.string(),
    currentPage: v.string(),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db
      .query("presence")
      .withIndex("by_user", (q) => q.eq("userId", args.userId))
      .unique();

    if (existing) {
      await ctx.db.patch(existing._id, {
        lastSeen: Date.now(),
        currentPage: args.currentPage,
      });
    } else {
      await ctx.db.insert("presence", {
        ...args,
        lastSeen: Date.now(),
      });
    }
  },
});

export const getOnlineUsers = query({
  args: { organizationId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("presence")
      .withIndex("by_org", (q) => q.eq("organizationId", args.organizationId))
      .filter((q) => q.gt(q.field("lastSeen"), Date.now() - 60000))
      .collect();
  },
});

Acceptance Criteria

Frontend
  • Real-time Presence workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Real-time Presence as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

File Storage

Uploading Files

// convex/files.ts
export const generateUploadUrl = mutation(async (ctx) => {
  return await ctx.storage.generateUploadUrl();
});

export const saveFile = mutation({
  args: {
    storageId: v.id("_storage"),
    fileName: v.string(),
    fileType: v.string(),
    uploadedBy: v.string(),
    organizationId: v.string(),
  },
  handler: async (ctx, args) => {
    return await ctx.db.insert("files", {
      ...args,
      uploadedAt: Date.now(),
    });
  },
});
// React upload component
function FileUploader() {
  const generateUploadUrl = useMutation(api.files.generateUploadUrl);
  const saveFile = useMutation(api.files.saveFile);

  const handleUpload = async (file: File) => {
    // Get upload URL
    const uploadUrl = await generateUploadUrl();
    
    // Upload file to Convex storage
    const result = await fetch(uploadUrl, {
      method: "POST",
      headers: { "Content-Type": file.type },
      body: file,
    });
    const { storageId } = await result.json();
    
    // Save file metadata
    await saveFile({
      storageId,
      fileName: file.name,
      fileType: file.type,
      uploadedBy: currentUser.id,
      organizationId: currentOrg.id,
    });
  };

  return <DropZone onDrop={handleUpload} />;
}

Acceptance Criteria

Frontend
  • Uploading Files workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Uploading Files as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Serving Files

// convex/files.ts
export const getFileUrl = query({
  args: { storageId: v.id("_storage") },
  handler: async (ctx, args) => {
    return await ctx.storage.getUrl(args.storageId);
  },
});

Acceptance Criteria

Frontend
  • Serving Files workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Serving Files as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Scheduled Functions

Cleanup Jobs

// convex/crons.ts
import { cronJobs } from "convex/server";
import { internal } from "./_generated/api";

const crons = cronJobs();

// Clean up old presence records every 5 minutes
crons.interval(
  "cleanup presence",
  { minutes: 5 },
  internal.presence.cleanup
);

// Clean up old notifications daily
crons.daily(
  "cleanup notifications",
  { hourUTC: 3, minuteUTC: 0 },
  internal.notifications.cleanup
);

export default crons;
// convex/presence.ts
export const cleanup = internalMutation({
  handler: async (ctx) => {
    const stalePresence = await ctx.db
      .query("presence")
      .filter((q) => q.lt(q.field("lastSeen"), Date.now() - 3600000))
      .collect();

    for (const record of stalePresence) {
      await ctx.db.delete(record._id);
    }
  },
});

Acceptance Criteria

Frontend
  • Cleanup Jobs workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Cleanup Jobs as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Webhooks to Symfony

Sync real-time data back to the main database:

// convex/sync.ts
import { httpAction } from "./_generated/server";

export const syncToSymfony = httpAction(async (ctx, request) => {
  const { event, data } = await request.json();

  // Forward to Symfony webhook endpoint
  const response = await fetch(process.env.SYMFONY_WEBHOOK_URL!, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Convex-Signature": generateSignature(data),
    },
    body: JSON.stringify({ event, data }),
  });

  return new Response(null, { status: response.status });
});

Schema Definition

// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  notifications: defineTable({
    userId: v.string(),
    organizationId: v.string(),
    type: v.string(),
    title: v.string(),
    message: v.string(),
    link: v.optional(v.string()),
    read: v.boolean(),
    createdAt: v.number(),
  })
    .index("by_user", ["userId"])
    .index("by_org", ["organizationId"]),

  presence: defineTable({
    userId: v.string(),
    organizationId: v.string(),
    currentPage: v.string(),
    lastSeen: v.number(),
  })
    .index("by_user", ["userId"])
    .index("by_org", ["organizationId"]),

  activity: defineTable({
    organizationId: v.string(),
    userId: v.string(),
    action: v.string(),
    entityType: v.string(),
    entityId: v.string(),
    metadata: v.optional(v.any()),
    createdAt: v.number(),
  })
    .index("by_org", ["organizationId"])
    .index("by_user", ["userId"]),

  files: defineTable({
    storageId: v.id("_storage"),
    fileName: v.string(),
    fileType: v.string(),
    uploadedBy: v.string(),
    organizationId: v.string(),
    uploadedAt: v.number(),
  })
    .index("by_org", ["organizationId"])
    .index("by_user", ["uploadedBy"]),
});

Authentication

Convex integrates with your existing auth:

// convex/auth.config.ts
export default {
  providers: [
    {
      domain: process.env.AUTH_DOMAIN,
      applicationID: process.env.AUTH_CLIENT_ID,
    },
  ],
};
// React provider setup
import { ConvexProviderWithAuth } from "convex/react";

function App() {
  return (
    <AuthProvider>
      <ConvexProviderWithAuth client={convex} useAuth={useAuthFromProvider}>
        <YourApp />
      </ConvexProviderWithAuth>
    </AuthProvider>
  );
}

Deployment

Development

# Start local development
npx convex dev

Acceptance Criteria

Frontend
  • Development workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Development as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Production

# Deploy to production
npx convex deploy --prod

Acceptance Criteria

Frontend
  • Production workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Production as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Environment Setup

# Set environment variables
npx convex env set SYMFONY_WEBHOOK_URL https://api.memberpulse.com/webhooks/convex
npx convex env set AUTH_DOMAIN your-auth-domain.com

Acceptance Criteria

Frontend
  • Environment Setup workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Environment Setup as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Monitoring

Convex Dashboard

Access at dashboard.convex.dev:

  • Real-time function logs
  • Database explorer
  • Storage usage
  • Performance metrics
  • Error tracking

Acceptance Criteria

Frontend
  • Convex Dashboard workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Convex Dashboard as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Metrics to Monitor

MetricTargetAlert Threshold
Query latency (p50)< 50ms> 200ms
Mutation latency (p50)< 100ms> 500ms
Active connections-> 10,000
Storage usage-> 80% quota
Function errors< 0.1%> 1%

Acceptance Criteria

Frontend
  • Metrics to Monitor workflow is implemented in the UI as described.
Backend / API
  • Backend behavior supports Metrics to Monitor as documented.
Permissions
  • Access is restricted per the Capabilities matrix on this page (or equivalent role rules).
Business Rules
  • All business rules for this feature are enforced.
Error Handling
  • Error states return clear messages and appropriate HTTP status codes.

Troubleshooting

Real-time updates not working
  • Verify NEXT_PUBLIC_CONVEX_URL is correct
  • Check browser console for WebSocket errors
  • Ensure user is authenticated
  • Verify query is subscribed (not just called once)
File uploads failing
  • Check file size limits (default 20MB)
  • Verify storage quota not exceeded
  • Ensure generateUploadUrl is called before upload
  • Check CORS configuration if uploading from browser
Functions timing out
  • Convex functions have 60-second timeout
  • Break large operations into smaller chunks
  • Use scheduled functions for long-running tasks
  • Check for infinite loops in queries

Security

Never expose your Convex deploy key in client-side code. Use environment variables for all sensitive configuration.

  • All data encrypted at rest and in transit
  • Row-level security via authentication
  • Automatic input validation via schema
  • Rate limiting built-in
  • GDPR-compliant data handling

Features

Convex Integration

Acceptance Criteria

Frontend
  • Developer-facing configuration and usage is documented and internally consistent.
Backend / API
  • Convex implementation matches the rules and contracts described on this page.
Permissions
  • Tenant scoping and access controls are enforced as described.
Business Rules
  • Domain rules/invariants are enforced as described.
Error Handling
  • Access violations and validation failures produce deterministic errors.

On this page

OverviewWhy Convex?PrerequisitesConfigurationEnvironment VariablesAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingPlatform SettingsAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingArchitectureData FlowAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingHybrid ArchitectureAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingReal-time FeaturesLive NotificationsAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingLive Dashboard MetricsAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingReal-time PresenceAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingFile StorageUploading FilesAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingServing FilesAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingScheduled FunctionsCleanup JobsAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingWebhooks to SymfonySchema DefinitionAuthenticationDeploymentDevelopmentAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingProductionAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingEnvironment SetupAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingMonitoringConvex DashboardAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingMetrics to MonitorAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError HandlingTroubleshootingSecurityFeaturesConvex IntegrationAcceptance CriteriaFrontendBackend / APIPermissionsBusiness RulesError Handling