Skip to content

GraphQL Integration for Community Enhancements

This document describes the GraphQL queries, mutations, and subscriptions needed to support the community enhancement features.

Overview

The community enhancements require backend support for:

  • Extended community data (badges, health scores, activity metrics)
  • Ally relationship system
  • Community feed and posts
  • Activity tracking
  • Personalized recommendations
  • Real-time updates

Backend Schema Requirements

Community Type Extensions

graphql
type Community {
  publicId: ID!
  name: String!
  description: String
  imageUrl: String
  coverImage: String          # NEW: Separate cover image
  category: String!            # NEW: Category string
  privacy: String!             # NEW: "public" or "private"
  guidelines: String           # NEW: Community guidelines
  createdAt: String!

  # Enhancement fields
  isVerified: Boolean          # NEW: Verified badge
  isFeatured: Boolean          # NEW: Featured status
  activeMembers: Int           # NEW: Count of active members (last 7 days)
  totalGoals: Int              # NEW: Total community goals
  growthRate: Float            # NEW: Growth rate percentage
  healthScore: Int             # NEW: 0-100 health score

  # Existing fields
  members: [CommunityMember!]!
  goals: [Goal!]!
  feedItems: [FeedItem!]!
  goalCategory: GoalCategory
}

type CommunityMember {
  id: ID!
  user: User!
  role: String!                # "admin", "moderator", "member"
  points: Int!                 # Community points earned
  rank: Int                    # Leaderboard rank
  joinedDate: String!
  goalsCompleted: Int!
  postsCount: Int!
  helpfulCount: Int!           # Number of helpful reactions
}

type CommunityBadges {
  verified: Boolean!
  topActive: Boolean!
  streak100: Boolean!
  featured: Boolean!
  club1k: Boolean!
  wins500: Boolean!
  earlySupporter: Boolean!
  topContributor: Boolean!
  perfectMonth: Boolean!
  allyMagnet: Boolean!
}

Ally Activity Feed

graphql
type AllyActivity {
  id: ID!
  allyId: ID!
  allyName: String!
  allyPhoto: String
  action: String!              # "joined", "posted", "achieved", "started"
  target: String!              # Description of what they did
  communityId: ID
  communityName: String
  timestamp: String!
}

Community Insights

graphql
type CommunityInsights {
  communitiesJoined: Int!
  postsThisWeek: Int!
  totalEngagement: Int!
  achievementsUnlocked: Int!

  suggestedCommunities: [SuggestedCommunity!]!
  trendingCommunities: [TrendingCommunity!]!
  upcomingEvents: [CommunityEvent!]!
  yourActivity: [UserActivity!]!
}

type SuggestedCommunity {
  publicId: ID!
  name: String!
  description: String
  category: String!
  coverImage: String
  members: [User!]!
  activeMembers: Int!
  mutualAllies: Int!           # Number of user's allies in this community
  matchScore: Int!             # 0-100 match score for recommendation
}

type TrendingCommunity {
  publicId: ID!
  name: String!
  description: String
  category: String!
  coverImage: String
  growthRate: Float!
  activeMembers: Int!
  totalGoals: Int!
  totalMembers: Int!
  isTrending: Boolean!
}

type CommunityEvent {
  id: ID!
  title: String!
  description: String
  communityId: ID!
  communityName: String!
  date: String!
  attendeeCount: Int!
}

type UserActivity {
  id: ID!
  type: String!                # "post", "comment", "goal", "achievement"
  content: String!
  communityId: ID
  communityName: String
  timestamp: String!
}

Community Feed

graphql
type CommunityPost {
  id: ID!
  type: String!                # "discussion", "achievement", "milestone"
  content: String!
  communityId: ID!
  userId: ID!
  userName: String!
  userPhoto: String
  goalId: ID
  goalName: String
  timestamp: String!
  likes: Int!
  comments: [PostComment!]!
}

type PostComment {
  id: ID!
  content: String!
  userId: ID!
  userName: String!
  timestamp: String!
}

Queries

Enhanced Communities Query

graphql
query CommunitiesQuery {
  communities {
    publicId
    name
    description
    imageUrl
    coverImage
    category
    createdAt
    isVerified
    isFeatured
    privacy
    activeMembers
    totalGoals
    growthRate
    healthScore
    members {
      id
    }
    goalCategory {
      id
      name
    }
  }
}

Ally Activity Feed Query

graphql
query AllyActivityFeedQuery($userId: ID!, $limit: Int) {
  allyActivityFeed(userId: $userId, limit: $limit) {
    id
    allyId
    allyName
    allyPhoto
    action
    target
    communityId
    communityName
    timestamp
  }
}

Community Insights Query

graphql
query CommunityInsightsQuery($userId: ID!) {
  communityInsights(userId: $userId) {
    communitiesJoined
    postsThisWeek
    totalEngagement
    achievementsUnlocked

    suggestedCommunities {
      publicId
      name
      description
      category
      coverImage
      members { id }
      activeMembers
      mutualAllies
      matchScore
    }

    trendingCommunities {
      publicId
      name
      description
      category
      coverImage
      growthRate
      activeMembers
      totalGoals
    }

    upcomingEvents {
      id
      title
      description
      communityId
      communityName
      date
      attendeeCount
    }

    yourActivity {
      id
      type
      content
      communityId
      communityName
      timestamp
    }
  }
}

Community Badges Query

graphql
query CommunityBadgesQuery($communityId: ID!) {
  communityBadges(communityId: $communityId) {
    verified
    topActive
    streak100
    featured
    club1k
    wins500
    earlySupporter
    topContributor
    perfectMonth
    allyMagnet
  }
}

Community Feed Query

graphql
query CommunityFeedQuery($communityId: ID!, $limit: Int, $offset: Int) {
  communityFeed(communityId: $communityId, limit: $limit, offset: $offset) {
    id
    type
    content
    communityId
    userId
    userName
    userPhoto
    goalId
    goalName
    timestamp
    likes
    comments {
      id
      content
      userId
      userName
      timestamp
    }
  }
}

Community Members Query

graphql
query CommunityMembersQuery($communityId: ID!) {
  communityMembers(communityId: $communityId) {
    user {
      publicId
      firstName
      lastName
      username
      photo { imageUrl }
    }
    role
    points
    rank
    joinedDate
    goalsCompleted
    postsCount
    helpfulCount
  }
}

Community Goals Query

graphql
query CommunityGoalsQuery($communityId: ID!) {
  communityGoals(communityId: $communityId) {
    publicId
    name
    content
    targetDateTime
    completed
    completedAtTime
    currentAmount
    targetAmount
    unit
    imageUrl
    category { name }
    user {
      publicId
      firstName
      lastName
      photo { imageUrl }
    }
    allEvents {
      publicId
      createdAtTime
    }
  }
}
graphql
query TrendingCommunitiesQuery($limit: Int) {
  trendingCommunities(limit: $limit) {
    publicId
    name
    description
    category
    coverImage
    growthRate
    activeMembers
    totalGoals
    totalMembers
    isTrending
  }
}
graphql
query RecommendedCommunitiesQuery($userId: ID!, $limit: Int) {
  recommendedCommunities(userId: $userId, limit: $limit) {
    publicId
    name
    description
    category
    coverImage
    activeMembers
    totalGoals
    totalMembers
    mutualAllies
    matchScore
    isRecommended
  }
}

User Allies Query

graphql
query UserAlliesQuery($userId: ID!) {
  userAllies(userId: $userId) {
    id
    publicId
    firstName
    lastName
    username
    photo { imageUrl }
    mutualCount
  }
}

Mutations

Leave Community Mutation

graphql
mutation LeaveCommunityMutation($userId: ID!, $communityId: ID!) {
  leaveCommunity(userId: $userId, communityId: $communityId) {
    result {
      success
    }
  }
}

Follow Community Mutation

graphql
mutation FollowCommunityMutation($userId: ID!, $communityId: ID!) {
  followCommunity(userId: $userId, communityId: $communityId) {
    result {
      success
    }
  }
}

Unfollow Community Mutation

graphql
mutation UnfollowCommunityMutation($userId: ID!, $communityId: ID!) {
  unfollowCommunity(userId: $userId, communityId: $communityId) {
    result {
      success
    }
  }
}

Create Community Mutation

graphql
mutation CreateCommunityMutation(
  $name: String!
  $description: String!
  $category: String!
  $privacy: String!
  $coverImageUrl: String
  $guidelines: String
) {
  createCommunity(
    name: $name
    description: $description
    category: $category
    privacy: $privacy
    coverImageUrl: $coverImageUrl
    guidelines: $guidelines
  ) {
    community {
      publicId
      name
      description
      category
      privacy
      coverImage
      guidelines
      createdAt
    }
  }
}

Create Community Post Mutation

graphql
mutation CreateCommunityPostMutation(
  $communityId: ID!
  $userId: ID!
  $content: String!
  $type: String!
) {
  createCommunityPost(
    communityId: $communityId
    userId: $userId
    content: $content
    type: $type
  ) {
    post {
      id
      type
      content
      userId
      userName
      timestamp
    }
  }
}

Subscriptions (Optional for Real-time)

Ally Activity Subscription

graphql
subscription AllyActivitySubscription($userId: ID!) {
  allyActivityUpdate(userId: $userId) {
    id
    allyId
    allyName
    allyPhoto
    action
    target
    communityId
    communityName
    timestamp
  }
}

Community Feed Subscription

graphql
subscription CommunityFeedSubscription($communityId: ID!) {
  communityFeedUpdate(communityId: $communityId) {
    id
    type
    content
    userId
    userName
    userPhoto
    timestamp
  }
}

Backend Implementation Notes

Health Score Calculation

The community health score (0-100) should be calculated based on:

python
def calculate_health_score(community):
    # Active member ratio (40%)
    total_members = len(community.members)
    active_members = count_active_last_7_days(community)
    active_ratio = (active_members / total_members) * 100 if total_members > 0 else 0

    # Post frequency (30%)
    posts_last_week = count_posts_last_7_days(community)
    post_score = min(100, posts_last_week * 10)  # Max at 10+ posts

    # Response time (20%)
    avg_response_time = get_avg_response_time_hours(community)
    response_score = max(0, 100 - (avg_response_time * 5))  # Penalty after 2 hours

    # Admin presence (10%)
    admin_active = has_admin_activity_last_7_days(community)
    admin_score = 100 if admin_active else 0

    health_score = (
        active_ratio * 0.4 +
        post_score * 0.3 +
        response_score * 0.2 +
        admin_score * 0.1
    )

    return round(health_score)

Growth Rate Calculation

python
def calculate_growth_rate(community):
    members_last_week = count_members_7_days_ago(community)
    current_members = len(community.members)

    if members_last_week == 0:
        return 100 if current_members > 0 else 0

    growth_rate = ((current_members - members_last_week) / members_last_week) * 100
    return round(growth_rate, 1)

Match Score for Recommendations

python
def calculate_match_score(user, community):
    score = 0

    # Mutual allies (40%)
    mutual_allies = count_mutual_allies(user, community)
    ally_score = min(100, mutual_allies * 20)  # Max at 5 allies
    score += ally_score * 0.4

    # Shared interests/goal categories (30%)
    user_categories = get_user_goal_categories(user)
    community_category = community.category
    if community_category in user_categories:
        score += 100 * 0.3

    # Activity level match (20%)
    user_activity = get_user_activity_level(user)
    community_activity = community.active_members / len(community.members)
    activity_diff = abs(user_activity - community_activity)
    activity_score = max(0, 100 - (activity_diff * 200))
    score += activity_score * 0.2

    # Community health (10%)
    score += community.health_score * 0.1

    return round(score)

Badge Calculation Logic

python
def calculate_community_badges(community):
    badges = {
        'verified': community.is_verified,
        'featured': community.is_featured,
        'topActive': community.health_score >= 80,
        'streak100': has_100_day_streak(community),
        'club1k': len(community.members) >= 1000,
        'wins500': count_completed_goals(community) >= 500,
        'earlySupporter': community.created_at < one_year_ago(),
        'topContributor': community.rank <= 10,
        'perfectMonth': has_perfect_30_days(community),
        'allyMagnet': avg_allies_per_member(community) >= 5
    }
    return badges

Database Migrations Required

New Tables

sql
-- Community follow relationships (non-members)
CREATE TABLE community_follows (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  community_id INTEGER REFERENCES communities(id),
  created_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(user_id, community_id)
);

-- Community posts
CREATE TABLE community_posts (
  id SERIAL PRIMARY KEY,
  community_id INTEGER REFERENCES communities(id),
  user_id INTEGER REFERENCES users(id),
  type VARCHAR(50) NOT NULL, -- discussion, achievement, milestone
  content TEXT NOT NULL,
  goal_id INTEGER REFERENCES goals(id),
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Post reactions
CREATE TABLE post_reactions (
  id SERIAL PRIMARY KEY,
  post_id INTEGER REFERENCES community_posts(id),
  user_id INTEGER REFERENCES users(id),
  type VARCHAR(50) DEFAULT 'like',
  created_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(post_id, user_id, type)
);

-- Post comments
CREATE TABLE post_comments (
  id SERIAL PRIMARY KEY,
  post_id INTEGER REFERENCES community_posts(id),
  user_id INTEGER REFERENCES users(id),
  content TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Community events
CREATE TABLE community_events (
  id SERIAL PRIMARY KEY,
  community_id INTEGER REFERENCES communities(id),
  title VARCHAR(255) NOT NULL,
  description TEXT,
  event_date TIMESTAMP NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Event attendance
CREATE TABLE event_attendees (
  id SERIAL PRIMARY KEY,
  event_id INTEGER REFERENCES community_events(id),
  user_id INTEGER REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(event_id, user_id)
);

-- Ally relationships
CREATE TABLE user_allies (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  ally_id INTEGER REFERENCES users(id),
  status VARCHAR(50) DEFAULT 'pending', -- pending, accepted, blocked
  created_at TIMESTAMP DEFAULT NOW(),
  accepted_at TIMESTAMP,
  UNIQUE(user_id, ally_id)
);

Column Additions

sql
-- Add to communities table
ALTER TABLE communities ADD COLUMN cover_image VARCHAR(500);
ALTER TABLE communities ADD COLUMN category VARCHAR(100);
ALTER TABLE communities ADD COLUMN privacy VARCHAR(50) DEFAULT 'public';
ALTER TABLE communities ADD COLUMN guidelines TEXT;
ALTER TABLE communities ADD COLUMN is_verified BOOLEAN DEFAULT FALSE;
ALTER TABLE communities ADD COLUMN is_featured BOOLEAN DEFAULT FALSE;

-- Add to community_members table
ALTER TABLE community_members ADD COLUMN role VARCHAR(50) DEFAULT 'member';
ALTER TABLE community_members ADD COLUMN points INTEGER DEFAULT 0;
ALTER TABLE community_members ADD COLUMN joined_date TIMESTAMP DEFAULT NOW();

Using the Composable

typescript
import { useCommunity } from '@/composables/useCommunity'

// In your component
const {
  useCommunitiesQuery,
  useAllyActivityFeed,
  useCommunityInsights,
  joinCommunity,
  leaveCommunity,
  createCommunity
} = useCommunity()

// Fetch communities
const { result, loading, error, refetch } = useCommunitiesQuery()
const communities = computed(() => result.value?.communities || [])

// Fetch ally activity
const { result: activityResult } = useAllyActivityFeed(userId, 10)
const activities = computed(() => activityResult.value?.allyActivityFeed || [])

// Join community
await joinCommunity(userId, communityId)

// Create community
const newCommunity = await createCommunity({
  name: 'My Community',
  description: 'A great community',
  category: 'fitness',
  privacy: 'public'
})

Testing

Use GraphQL Playground or Apollo Studio to test queries:

graphql
# Test query
query {
  communityInsights(userId: "123") {
    communitiesJoined
    suggestedCommunities {
      name
      mutualAllies
      matchScore
    }
  }
}

Performance Considerations

  1. Caching: Use Apollo cache-and-network policy for real-time data
  2. Pagination: Implement cursor-based pagination for feeds
  3. Batch Loading: Use DataLoader for N+1 query prevention
  4. Indexing: Add database indexes on frequently queried fields:
    • community_members(user_id, community_id)
    • community_posts(community_id, created_at)
    • user_allies(user_id, status)
  5. Computed Fields: Cache health_score, growth_rate calculations
  6. Rate Limiting: Implement rate limiting on mutations

Next Steps

  1. Implement backend resolvers for all queries/mutations
  2. Add database migrations
  3. Implement health score/match score calculations
  4. Add subscription support for real-time updates
  5. Implement caching strategy
  6. Add comprehensive error handling
  7. Write integration tests

Last updated: 2026-05-04

Loading…