Adding Likes to Your Social Graph
Likes are a fundamental engagement mechanism in social applications. The Tapestry Likes API allows users to express appreciation for content and interact with posts. You can use either the socialfi package (recommended for simpler integration) or call the API directly.
Installing and Setting Up the Socialfi Package (Recommended)
We recommend using the socialfi package as it provides a cleaner, more maintainable approach:
npm install socialfi
import { SocialFi } from 'socialfi';
const API_URL = 'https://api.usetapestry.dev/v1/'; // tapestry prod URL
// const API_URL = 'https://api.dev.usetapestry.dev/v1/'; // tapestry dev URL
const API_KEY = process.env.TAPESTRY_API_KEY; // Get your API key from https://app.usetapestry.dev/
const client = new SocialFi({
baseURL: API_URL,
apiKey: API_KEY,
});
Creating a Like
To allow users to like a post or content, use the like creation endpoint:
Using the Socialfi Package
try {
const newLike = await client.likes.createLike(
{
apiKey: API_KEY,
},
{
profileId: 'user123', // The ID of the profile creating the like
contentId: 'post456', // The ID of the content being liked
blockchain: 'SOLANA',
execution: 'FAST_UNCONFIRMED'
}
);
console.log('Like created:', newLike);
} catch (error) {
console.error('Error creating like:', error);
}
Using the API Directly
try {
const response = await fetch('https://api.usetapestry.dev/v1/likes?apiKey=YOUR_API_KEY', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
profileId: 'user123',
contentId: 'post456',
blockchain: 'SOLANA',
execution: 'FAST_UNCONFIRMED'
}),
});
const newLike = await response.json();
console.log('Like created:', newLike);
} catch (error) {
console.error('Error creating like:', error);
}
Request Parameters
Field | Type | Description |
---|---|---|
profileId | string | The ID of the profile creating the like. |
contentId | string | The ID of the content being liked. |
blockchain | string | The blockchain to use (default: SOLANA) |
execution | string | Execution method (default: FAST_UNCONFIRMED) |
Response Example
{
"like": {
"id": "like-1234567890",
"profileId": "user123",
"contentId": "post456",
"blockchain": "SOLANA",
"createdAt": "2024-01-15T10:30:00Z"
},
"user": {
"id": "user123",
"username": "johndoe",
"profileImage": "https://example.com/avatar.jpg"
}
}
Removing a Like (Unlike)
To allow users to unlike content they previously liked:
Using the Socialfi Package
try {
const result = await client.likes.deleteLike(
{
apiKey: API_KEY,
},
{
profileId: 'user123',
contentId: 'post456',
blockchain: 'SOLANA',
execution: 'FAST_UNCONFIRMED'
}
);
console.log('Like removed:', result);
} catch (error) {
console.error('Error removing like:', error);
}
Using the API Directly
try {
const response = await fetch('https://api.usetapestry.dev/v1/likes?apiKey=YOUR_API_KEY', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
profileId: 'user123',
contentId: 'post456',
blockchain: 'SOLANA',
execution: 'FAST_UNCONFIRMED'
}),
});
const result = await response.json();
console.log('Like removed:', result);
} catch (error) {
console.error('Error removing like:', error);
}
Response Example
{
"success": true,
"message": "Like removed successfully"
}
Getting Likes for Content
To retrieve all likes for a specific piece of content:
Using the Socialfi Package
try {
const likes = await client.likes.getLikesByContent(
{
apiKey: API_KEY,
contentId: 'post456',
limit: 20,
offset: 0
}
);
console.log('Likes for content:', likes);
} catch (error) {
console.error('Error fetching likes:', error);
}
Using the API Directly
try {
const response = await fetch(
'https://api.usetapestry.dev/v1/likes/content/post456?apiKey=YOUR_API_KEY&limit=20&offset=0'
);
const likes = await response.json();
console.log('Likes for content:', likes);
} catch (error) {
console.error('Error fetching likes:', error);
}
Query Parameters
Parameter | Type | Description |
---|---|---|
contentId | string | The ID of the content to get likes for. |
limit | number | Maximum number of likes to return. |
offset | number | Number of likes to skip. |
Response Example
{
"likes": [
{
"like": {
"id": "like-1234567890",
"createdAt": "2024-01-15T10:30:00Z"
},
"user": {
"id": "user123",
"username": "johndoe",
"profileImage": "https://example.com/avatar1.jpg"
}
},
{
"like": {
"id": "like-0987654321",
"createdAt": "2024-01-15T11:00:00Z"
},
"user": {
"id": "user789",
"username": "janedoe",
"profileImage": "https://example.com/avatar2.jpg"
}
}
],
"pagination": {
"total": 45,
"limit": 20,
"offset": 0,
"hasMore": true
},
"likeCount": 45
}
Getting Likes by User
To retrieve all content that a specific user has liked:
Using the Socialfi Package
try {
const userLikes = await client.likes.getLikesByProfile(
{
apiKey: API_KEY,
profileId: 'user123',
limit: 10,
offset: 0
}
);
console.log('Content liked by user:', userLikes);
} catch (error) {
console.error('Error fetching user likes:', error);
}
Using the API Directly
try {
const response = await fetch(
'https://api.usetapestry.dev/v1/likes/profile/user123?apiKey=YOUR_API_KEY&limit=10&offset=0'
);
const userLikes = await response.json();
console.log('Content liked by user:', userLikes);
} catch (error) {
console.error('Error fetching user likes:', error);
}
Response Example
{
"likes": [
{
"like": {
"id": "like-1234567890",
"createdAt": "2024-01-15T10:30:00Z"
},
"content": {
"id": "post456",
"content": "Amazing post about blockchain technology!",
"author": {
"id": "user789",
"username": "contentcreator"
}
}
}
],
"pagination": {
"total": 25,
"limit": 10,
"offset": 0,
"hasMore": true
}
}
Checking if User Liked Content
To check if a specific user has liked a specific piece of content:
Using the Socialfi Package
try {
const hasLiked = await client.likes.checkUserLikedContent(
{
apiKey: API_KEY,
profileId: 'user123',
contentId: 'post456'
}
);
console.log('User has liked this content:', hasLiked.hasLiked);
} catch (error) {
console.error('Error checking like status:', error);
}
Using the API Directly
try {
const response = await fetch(
'https://api.usetapestry.dev/v1/likes/check?apiKey=YOUR_API_KEY&profileId=user123&contentId=post456'
);
const hasLiked = await response.json();
console.log('User has liked this content:', hasLiked.hasLiked);
} catch (error) {
console.error('Error checking like status:', error);
}
Response Example
{
"hasLiked": true,
"likeId": "like-1234567890",
"likedAt": "2024-01-15T10:30:00Z"
}
Like Counts
To get just the like count for content without fetching all individual likes:
Using the Socialfi Package
try {
const likeCount = await client.likes.getLikeCount(
{
apiKey: API_KEY,
contentId: 'post456'
}
);
console.log('Like count:', likeCount.count);
} catch (error) {
console.error('Error fetching like count:', error);
}
Using the API Directly
try {
const response = await fetch(
'https://api.usetapestry.dev/v1/likes/count/post456?apiKey=YOUR_API_KEY'
);
const likeCount = await response.json();
console.log('Like count:', likeCount.count);
} catch (error) {
console.error('Error fetching like count:', error);
}
Response Example
{
"contentId": "post456",
"count": 45,
"lastUpdated": "2024-01-15T11:30:00Z"
}
Building Like Functionality
Here's a complete example of how to implement like/unlike functionality in your app using the socialfi package:
interface LikeButtonProps {
contentId: string;
currentUserId: string;
}
class LikeButton {
private contentId: string;
private currentUserId: string;
private isLiked: boolean = false;
private likeCount: number = 0;
constructor(props: LikeButtonProps) {
this.contentId = props.contentId;
this.currentUserId = props.currentUserId;
this.initializeLikeStatus();
}
async initializeLikeStatus() {
try {
// Check if user has liked this content
const likeStatus = await client.likes.checkUserLikedContent({
apiKey: API_KEY,
profileId: this.currentUserId,
contentId: this.contentId
});
// Get current like count
const likeCountData = await client.likes.getLikeCount({
apiKey: API_KEY,
contentId: this.contentId
});
this.isLiked = likeStatus.hasLiked;
this.likeCount = likeCountData.count;
this.updateUI();
} catch (error) {
console.error('Error initializing like status:', error);
}
}
async toggleLike() {
try {
if (this.isLiked) {
// Unlike the content
await client.likes.deleteLike(
{
apiKey: API_KEY,
},
{
profileId: this.currentUserId,
contentId: this.contentId,
blockchain: 'SOLANA'
}
);
this.isLiked = false;
this.likeCount--;
} else {
// Like the content
await client.likes.createLike(
{
apiKey: API_KEY,
},
{
profileId: this.currentUserId,
contentId: this.contentId,
blockchain: 'SOLANA'
}
);
this.isLiked = true;
this.likeCount++;
}
this.updateUI();
} catch (error) {
console.error('Error toggling like:', error);
// Revert optimistic update on error
this.initializeLikeStatus();
}
}
private updateUI() {
// Update your UI elements here
console.log(`Like button - Liked: ${this.isLiked}, Count: ${this.likeCount}`);
}
}
Real-time Like Updates
For real-time like updates, you can implement polling or use WebSocket connections:
// Polling example
async function pollLikeUpdates(contentId: string, intervalMs: number = 5000) {
setInterval(async () => {
try {
const likeCount = await client.likes.getLikeCount({
apiKey: API_KEY,
contentId: contentId
});
// Update your UI with the new count
updateLikeCountInUI(contentId, likeCount.count);
} catch (error) {
console.error('Error polling like updates:', error);
}
}, intervalMs);
}
Best Practices
When implementing likes functionality:
- Optimistic Updates: Update the UI immediately when a user clicks like/unlike, then sync with the server
- Debouncing: Implement debouncing to prevent rapid like/unlike actions that could spam the API
- Error Handling: Always handle API errors gracefully and revert optimistic updates when necessary
- Rate Limiting: Respect API rate limits to prevent service disruption
- Authentication: Ensure users are authenticated before allowing like actions
- Validation: Validate that content exists before allowing likes
- Privacy: Respect user privacy settings regarding like visibility
- Performance: Use pagination for like lists and implement lazy loading for better performance
- Accessibility: Ensure like buttons are accessible with proper ARIA labels and keyboard navigation