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
}
}
}Trending Communities Query
graphql
query TrendingCommunitiesQuery($limit: Int) {
trendingCommunities(limit: $limit) {
publicId
name
description
category
coverImage
growthRate
activeMembers
totalGoals
totalMembers
isTrending
}
}Recommended Communities Query
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 badgesDatabase 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
- Caching: Use Apollo cache-and-network policy for real-time data
- Pagination: Implement cursor-based pagination for feeds
- Batch Loading: Use DataLoader for N+1 query prevention
- 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)
- Computed Fields: Cache health_score, growth_rate calculations
- Rate Limiting: Implement rate limiting on mutations
Next Steps
- Implement backend resolvers for all queries/mutations
- Add database migrations
- Implement health score/match score calculations
- Add subscription support for real-time updates
- Implement caching strategy
- Add comprehensive error handling
- Write integration tests
Last updated: 2026-05-04