This guide covers how to integrate and use the new User Profiles feature in your StrellerMinds Backend application.
- Node.js v18+
- PostgreSQL v12+
- Existing StrellerMinds Backend with authentication setup
npm installnpm run migration:runOr if migrations aren't set to auto-run:
npm run typeorm migration:runnpm run start:devWhen a new user registers, you'll need to create their profile. Update the auth service:
// In auth.service.ts after user creation
async register(registerDto: RegisterDto): Promise<UserResponse> {
// ... existing code ...
// Create user profile
await this.userProfileService.createProfile(user.id);
// ... rest of code ...
}import { UserProfileService } from './services/user-profile.service';
constructor(private userProfileService: UserProfileService) {}
// Get user profile
const profile = await this.userProfileService.getProfileByUserId(userId);
// Update profile
const updated = await this.userProfileService.updateProfile(userId, updateDto);
// Get profile with all details
const fullProfile = await this.userProfileService.getProfileWithDetails(userId);
// Track profile view
await this.userProfileService.trackProfileView(profileId, referrer);import { PortfolioService } from './services/portfolio.service';
constructor(private portfolioService: PortfolioService) {}
// Create portfolio item
const item = await this.portfolioService.createPortfolioItem(profileId, createDto);
// Get items
const items = await this.portfolioService.getPortfolioItems(profileId);
// Get featured items
const featured = await this.portfolioService.getFeaturedItems(profileId);
// Track views
await this.portfolioService.trackPortfolioView(itemId);import { SocialService } from './services/social.service';
constructor(private socialService: SocialService) {}
// Follow user
await this.socialService.followUser(followerProfileId, followingProfileId);
// Get followers
const followers = await this.socialService.getFollowers(profileId);
// Get network
const network = await this.socialService.getUserNetwork(profileId);
// Check if following
const isFollowing = await this.socialService.isFollowing(followerId, followingId);
// Block user
await this.socialService.blockUser(blockerProfileId, blockedProfileId);import { AchievementService } from './services/achievement.service';
constructor(private achievementService: AchievementService) {}
// Award badge
await this.achievementService.awardBadgeToUser(profileId, {
badgeId: '...',
unlockedReason: 'Completed blockchain course'
});
// Get badges
const badges = await this.achievementService.getUserBadges(profileId);
// Get leaderboard
const leaderboard = await this.achievementService.getLeaderboard(10);
// Get stats
const stats = await this.achievementService.getAchievementStats(profileId);import { PrivacyService } from './services/privacy.service';
constructor(private privacyService: PrivacyService) {}
// Get settings
const settings = await this.privacyService.getPrivacySettings(userId);
// Update settings
const updated = await this.privacyService.updatePrivacySettings(userId, updateDto);
// Block/mute
await this.privacyService.blockUser(userId, blockedUserId);
// Check if blocked
const isBlocked = await this.privacyService.isUserBlocked(userId, blockerId);
// Export data
const data = await this.privacyService.exportUserData(userId);curl -X PUT http://localhost:3000/api/profiles/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <JWT_TOKEN>" \
-d '{
"bio": "Passionate blockchain developer",
"headline": "Smart Contract Engineer",
"location": "San Francisco",
"skills": "Solidity, TypeScript, NestJS",
"specialization": "DeFi",
"yearsOfExperience": 5
}'curl -X POST http://localhost:3000/api/profiles/me/portfolio \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <JWT_TOKEN>" \
-d '{
"title": "DeFi Lending Protocol",
"description": "Smart contracts for lending on Stellar",
"type": "project",
"projectUrl": "https://github.com/user/defi-protocol",
"technologies": ["Soroban", "Rust", "TypeScript"],
"tags": ["DeFi", "Stellar", "Smart Contracts"],
"startDate": "2023-01-01",
"endDate": "2023-06-30"
}'curl -X POST http://localhost:3000/api/social/:userId/follow \
-H "Authorization: Bearer <JWT_TOKEN>"curl -X PUT http://localhost:3000/api/privacy/me/settings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <JWT_TOKEN>" \
-d '{
"profileVisibility": "public",
"portfolioVisibility": "friends-only",
"allowMessaging": true,
"emailNotifications": false
}'// Get full profile with details
const response = await fetch('/api/profiles/me/full', {
headers: { 'Authorization': `Bearer ${token}` }
});
const profile = await response.json();
// Access data
console.log(profile.bio);
console.log(profile.portfolioItems);
console.log(profile.badges);
console.log(profile.analytics);const response = await fetch('/api/profiles/me', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
bio: 'Updated bio',
headline: 'New headline'
})
});const response = await fetch(`/api/social/${userId}/follow`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});const response = await fetch('/api/social/me/network', {
headers: { 'Authorization': `Bearer ${token}` }
});
const network = await response.json();
console.log(network.followers);
console.log(network.following);
console.log(network.suggestedUsers);SELECT u.*, p.* FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
WHERE u.id = '<user_id>';SELECT pi.* FROM portfolio_items pi
JOIN user_profiles p ON pi.profile_id = p.id
WHERE p.user_id = '<user_id>'
ORDER BY pi.display_order ASC;SELECT p.* FROM user_profiles p
JOIN follows f ON p.id = f.follower_id
WHERE f.following_id = '<profile_id>' AND f.status = 'follow';SELECT b.*, ub.* FROM badges b
JOIN user_badges ub ON b.id = ub.badge_id
WHERE ub.profile_id = '<profile_id>'
ORDER BY ub.awarded_at DESC;# Create a scheduled job to reset daily views
npm run task:reset-daily-analytics// This is done automatically on profile update
// But you can manually trigger:
const profile = await userProfileService.getProfileByUserId(userId);
await userProfileService.updateProfile(userId, profile);// Archive analytics older than 1 year
// Can be implemented as a scheduled jobcurl -X PUT http://localhost:3000/api/profiles/me \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $(curl -s http://localhost:3000/api/auth/login -d '{"email":"test@example.com","password":"password"}' | jq -r '.accessToken')" \
-d '{
"bio": "Test bio",
"headline": "Test headline"
}'# Create portfolio item
curl -X POST http://localhost:3000/api/profiles/me/portfolio \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-d '{"title": "Test Project", "type": "project"}'
# Get portfolio
curl http://localhost:3000/api/profiles/me/portfolio \
-H "Authorization: Bearer <TOKEN>"# Follow user
curl -X POST http://localhost:3000/api/social/<USER_ID>/follow \
-H "Authorization: Bearer <TOKEN>"
# Get followers
curl http://localhost:3000/api/social/me/followers \
-H "Authorization: Bearer <TOKEN>"- Ensure PostgreSQL is running
- Check database credentials in
.env - Verify
typeormis properly installed
- Check user was properly created first
- Verify user ID exists in database
- Check migration ran successfully
- Ensure both users have profiles
- Check privacy settings allow following
- Verify users aren't blocked
- Ensure profile view endpoint is called
- Check ProfileAnalytics table was created
- Verify analytics service is injected
All critical columns are indexed:
user_profiles.user_idportfolio_items.profile_iduser_badges.profile_idfollows.follower_id,following_idprivacy_settings.profile_id
- Use
relationsin queries only when needed - Implement pagination for large result sets
- Cache leaderboard data
// Cache profile for 5 minutes
@Cacheable({
ttl: 5 * 60 * 1000
})
getProfileByUserId(userId: string) {
// ...
}- ✅ All endpoints require JWT authentication
- ✅ Users can only access their own data
- ✅ Privacy settings are enforced
- ✅ Block/mute functionality prevents access
- ✅ Input validation via DTOs
- ✅ GDPR data export available
- ✅ Rate limiting on sensitive endpoints
- ✅ SQL injection prevention via TypeORM
- Set
synchronize: falsein TypeORM config - Use proper database backups
- Enable read replicas for analytics queries
- Implement caching layer (Redis)
- Monitor query performance
- Set up alerting for data anomalies
- Regular data exports for compliance
- Test disaster recovery procedures
For more detailed documentation, see USER_PROFILES.md