Quick Start
Quick Start Guide
Get up and running with the Cloudillo API in minutes. This guide shows you how to accomplish common tasks.
Prerequisites
- Node.js 16+ installed
- A Cloudillo server instance (local or hosted)
- Basic JavaScript/TypeScript knowledge
Installation
npm install @cloudillo/base1. Initialize and Authenticate
Register a New Account
import * as cloudillo from '@cloudillo/base'
// Initialize the library
await cloudillo.init('my-app', {
serverUrl: 'https://your-server.com'
})
// Register new user
const registerResponse = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
idTag: 'alice@example.com',
password: 'secure-password-123',
name: 'Alice Johnson'
})
})
const { data } = await registerResponse.json()
console.log('Registered! Token:', data.token)
// Store token for future requests
localStorage.setItem('cloudillo_token', data.token)Login to Existing Account
const loginResponse = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
idTag: 'alice@example.com',
password: 'secure-password-123'
})
})
const { data } = await loginResponse.json()
localStorage.setItem('cloudillo_token', data.token)
// Create authenticated API client
const api = cloudillo.createApiClient({
token: data.token
})2. Get User Profile
// Get your own profile
const myProfile = await api.me.get()
console.log('My profile:', myProfile.data)
// {
// tnId: 12345,
// idTag: 'alice@example.com',
// name: 'Alice Johnson',
// profilePic: '/api/file/b1~abc123'
// }
3. Create a Post
// Create a simple text post
const post = await api.action.post({
type: 'POST',
content: {
text: 'Hello, Cloudillo! This is my first post.',
title: 'My First Post'
}
})
console.log('Post created:', post.data.actionId)Create a Post with Images
// First, upload an image
const fileInput = document.querySelector('input[type="file"]')
const imageFile = fileInput.files[0]
const uploadResponse = await fetch(`/api/file/default/${encodeURIComponent(imageFile.name)}?tags=post,photo`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': imageFile.type
},
body: imageFile
})
const { data: fileData } = await uploadResponse.json()
const fileId = fileData.fileId
// Create post with attachment
const postWithImage = await api.action.post({
type: 'POST',
content: {
text: 'Check out this photo!',
title: 'Beach Sunset'
},
attachments: [fileId]
})
console.log('Post with image:', postWithImage.data.actionId)4. Get Recent Posts
// Get recent posts from everyone
const posts = await api.action.get({
type: 'POST',
status: 'A',
_limit: 20,
_expand: 'issuer',
_sort: 'createdAt',
_order: 'desc'
})
posts.data.forEach(post => {
console.log(`${post.issuer.name}: ${post.content.text}`)
})
// Example output:
// Alice Johnson: Hello, Cloudillo!
// Bob Smith: Just joined!
// Carol Davis: Having a great day!
5. Comment on a Post
// Add a comment to a post
const comment = await api.action.post({
type: 'CMNT',
parentId: 'act_post123',
content: {
text: 'Great post! Thanks for sharing.'
}
})
console.log('Comment added:', comment.data.actionId)
// Get all comments on a post
const comments = await api.action.get({
type: 'CMNT',
parentId: 'act_post123',
_expand: 'issuer',
_sort: 'createdAt',
_order: 'asc'
})
console.log(`${comments.data.length} comments`)6. React to Content
// Add a reaction (like, love, etc.)
const reaction = await api.action.id('act_post123').reaction.post({
type: 'LOVE'
})
console.log('Reaction added:', reaction.data.actionId)
// Get actions with statistics
const post = await api.action.id('act_post123').get()
console.log('Statistics:', post.data.stat)
// {
// reactions: 15,
// comments: 8,
// ownReaction: 'LOVE'
// }
7. Follow a User
// Follow another user
const follow = await api.action.post({
type: 'FLLW',
subject: 'bob@example.com'
})
console.log('Now following bob@example.com')
// Get list of people you follow
const following = await api.action.get({
type: 'FLLW',
issuerTag: cloudillo.idTag,
status: 'A'
})
console.log(`Following ${following.data.length} users`)8. Upload and Manage Files
Upload Profile Picture
const imageInput = document.querySelector('input[type="file"]')
const profileImage = imageInput.files[0]
const response = await fetch('/api/me/image', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': profileImage.type
},
body: profileImage
})
const { data } = await response.json()
console.log('Profile picture updated:', data.profilePic)Upload File with Progress
async function uploadFileWithProgress(file, onProgress) {
const xhr = new XMLHttpRequest()
return new Promise((resolve, reject) => {
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100
onProgress(percent)
}
})
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText))
} else {
reject(new Error(xhr.statusText))
}
})
xhr.addEventListener('error', () => reject(new Error('Upload failed')))
xhr.open('POST', `/api/file/default/${encodeURIComponent(file.name)}`)
xhr.setRequestHeader('Authorization', `Bearer ${token}`)
xhr.setRequestHeader('Content-Type', file.type)
xhr.send(file)
})
}
// Usage
const result = await uploadFileWithProgress(file, (percent) => {
console.log(`Upload progress: ${percent.toFixed(1)}%`)
updateProgressBar(percent)
})
console.log('Upload complete:', result.data.fileId)List Your Files
// Get all your files
const files = await api.file.get({
fileTp: 'BLOB',
_limit: 50,
_sort: 'createdAt',
_order: 'desc'
})
files.data.forEach(file => {
console.log(`${file.fileName} - ${file.contentType}`)
})
// Filter by type
const images = await api.file.get({
fileTp: 'BLOB',
contentType: 'image/*'
})
console.log(`You have ${images.data.length} images`)9. Real-time Updates (WebSocket)
// Connect to WebSocket for real-time updates
const ws = new WebSocket(`wss://your-server.com/ws/bus`)
ws.onopen = () => {
console.log('Connected to real-time bus')
// Subscribe to events
ws.send(JSON.stringify({
type: 'subscribe',
channels: ['actions', 'messages']
}))
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
switch (data.type) {
case 'action':
console.log('New action:', data.action)
handleNewAction(data.action)
break
case 'message':
console.log('New message:', data.message)
showNotification(data.message)
break
}
}
ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
ws.onclose = () => {
console.log('Disconnected from real-time bus')
// Reconnect after delay
setTimeout(() => connectWebSocket(), 5000)
}10. Private Messaging
// Send a private message
const message = await api.action.post({
type: 'MSG',
subject: 'bob@example.com',
content: {
text: 'Hey Bob, how are you?',
subject: 'Checking in'
}
})
console.log('Message sent:', message.data.actionId)
// Get your messages
const messages = await api.action.get({
type: 'MSG',
involved: cloudillo.idTag,
_expand: 'issuer,subject',
_sort: 'createdAt',
_order: 'desc',
_limit: 50
})
console.log(`You have ${messages.data.length} messages`)
// Display messages
messages.data.forEach(msg => {
const from = msg.issuerTag === cloudillo.idTag ? 'You' : msg.issuer.name
const to = msg.subject === cloudillo.idTag ? 'You' : msg.subject
console.log(`${from} → ${to}: ${msg.content.text}`)
})11. Search and Filter
Search Posts by Text
// Get posts from the last week
const lastWeek = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60
const recentPosts = await api.action.get({
type: 'POST',
status: 'A',
createdAfter: lastWeek,
_expand: 'issuer',
_limit: 100
})
// Filter in client (for complex searches)
const searchTerm = 'cloudillo'
const matchingPosts = recentPosts.data.filter(post =>
post.content.text?.toLowerCase().includes(searchTerm)
)
console.log(`Found ${matchingPosts.length} posts mentioning "${searchTerm}"`)Get User’s Activity
// Get everything involving a specific user
const userActivity = await api.action.get({
involved: 'bob@example.com',
_expand: 'issuer',
_limit: 100,
_sort: 'createdAt',
_order: 'desc'
})
// Categorize by type
const byType = userActivity.data.reduce((acc, action) => {
acc[action.type] = (acc[action.type] || 0) + 1
return acc
}, {})
console.log('Activity breakdown:', byType)
// { POST: 15, CMNT: 32, REACT: 48, FLLW: 5 }
12. Share Files
// Share a file with another user
const share = await api.action.post({
type: 'FSHR',
subject: 'bob@example.com',
attachments: ['b1~abc123'],
content: {
permission: 'READ',
message: 'Check out this document!'
}
})
console.log('File shared:', share.data.actionId)
// Share with write permission
const shareWrite = await api.action.post({
type: 'FSHR',
subject: 'carol@example.com',
attachments: ['f1~xyz789'],
content: {
permission: 'WRITE',
message: 'Let\'s collaborate on this!'
}
})13. Update Profile
// Update your profile information
const updated = await api.me.patch({
name: 'Alice Johnson-Smith',
x: {
bio: 'Software developer and Cloudillo enthusiast',
location: 'San Francisco, CA',
website: 'https://alice.example.com',
twitter: '@alice'
}
})
console.log('Profile updated:', updated.data)14. Handle Errors Gracefully
async function createPostSafely(content) {
try {
const post = await api.action.post({
type: 'POST',
content
})
console.log('✓ Post created:', post.data.actionId)
return post.data
} catch (error) {
if (error.response) {
const { code, message } = error.response.data.error
switch (code) {
case 'E-AUTH-EXPIRED':
console.error('Session expired. Please login again.')
window.location.href = '/login'
break
case 'E-PERM-DENIED':
console.error('Permission denied:', message)
alert('You don\'t have permission to post')
break
case 'E-RATE-LIMIT':
console.error('Rate limited. Please slow down.')
alert('Too many posts. Please wait a moment.')
break
default:
console.error('Error creating post:', message)
alert('Failed to create post. Please try again.')
}
} else {
console.error('Network error:', error.message)
alert('Network error. Check your connection.')
}
throw error
}
}
// Usage
await createPostSafely({
text: 'Hello, world!',
title: 'My Post'
})15. Pagination
async function getAllPosts() {
const allPosts = []
let offset = 0
const limit = 50
while (true) {
const response = await api.action.get({
type: 'POST',
status: 'A',
_limit: limit,
_offset: offset,
_expand: 'issuer'
})
allPosts.push(...response.data)
console.log(`Loaded ${allPosts.length} of ${response.pagination.total} posts`)
if (!response.pagination.hasMore) {
break
}
offset += limit
}
return allPosts
}
// Load all posts (be careful with large datasets!)
const posts = await getAllPosts()
console.log(`Total posts loaded: ${posts.length}`)Complete Example: Simple Social Feed
import * as cloudillo from '@cloudillo/base'
class SocialFeed {
constructor(token) {
this.api = cloudillo.createApiClient({ token })
}
async initialize() {
// Get user profile
const profile = await this.api.me.get()
console.log('Logged in as:', profile.data.name)
}
async loadFeed(limit = 20) {
// Get recent posts with full profiles
const posts = await this.api.action.get({
type: 'POST',
status: 'A',
_limit: limit,
_expand: 'issuer',
_sort: 'createdAt',
_order: 'desc'
})
return posts.data
}
async createPost(text, title) {
const post = await this.api.action.post({
type: 'POST',
content: { text, title }
})
console.log('✓ Post created')
return post.data
}
async addComment(postId, text) {
const comment = await this.api.action.post({
type: 'CMNT',
parentId: postId,
content: { text }
})
console.log('✓ Comment added')
return comment.data
}
async addReaction(actionId, reactionType = 'LOVE') {
const reaction = await this.api.action.id(actionId).reaction.post({
type: reactionType
})
console.log('✓ Reaction added')
return reaction.data
}
async getComments(postId) {
const comments = await this.api.action.get({
type: 'CMNT',
parentId: postId,
_expand: 'issuer',
_sort: 'createdAt',
_order: 'asc'
})
return comments.data
}
displayPost(post) {
console.log('\n' + '='.repeat(60))
console.log(`${post.issuer.name} (@${post.issuerTag})`)
console.log(`${post.content.title}`)
console.log(post.content.text)
console.log(`❤️ ${post.stat?.reactions || 0} | 💬 ${post.stat?.comments || 0}`)
console.log('='.repeat(60))
}
}
// Usage
const feed = new SocialFeed(token)
await feed.initialize()
// Load and display feed
const posts = await feed.loadFeed(10)
posts.forEach(post => feed.displayPost(post))
// Create a new post
await feed.createPost(
'Just discovered Cloudillo - it\'s amazing!',
'First Impressions'
)
// Interact with a post
await feed.addReaction('act_post123', 'LOVE')
await feed.addComment('act_post123', 'Totally agree!')
// Get comments
const comments = await feed.getComments('act_post123')
console.log(`\nComments (${comments.length}):`)
comments.forEach(c => {
console.log(` ${c.issuer.name}: ${c.content.text}`)
})Next Steps
Now that you know the basics, explore:
- REST API Reference - Complete API documentation
- Actions API - Deep dive into social features
- Files API - File upload and management
- WebSocket API - Real-time features
- Error Handling - Handle errors gracefully
- Authentication - Advanced auth topics
Tips for Success
- Always handle errors - Network issues happen, be prepared
- Use pagination - Don’t load thousands of items at once
- Expand wisely - Only expand what you need to reduce payload size
- Cache when possible - Store user profiles, settings locally
- Test incrementally - Start small, add features gradually
- Monitor rate limits - Respect the API limits
- Validate input - Check data before sending to API
- Log requests - Keep request IDs for debugging
Common Gotchas
❌ Forgetting to add /api/ prefix
fetch('/action') // Wrong!
fetch('/api/action') // Correct
❌ Not handling authentication expiry
// Always check for auth errors and redirect to login
❌ Using wrong file upload endpoint
// Wrong: POST /file/upload
// Correct: POST /api/file/{preset}/{file_name}
❌ Not expanding relations
// Inefficient - requires N+1 queries
const actions = await api.action.get({ type: 'POST' })
for (const action of actions.data) {
const issuer = await api.profile.get({ idTag: action.issuerTag })
}
// Efficient - single query
const actions = await api.action.get({
type: 'POST',
_expand: 'issuer'
})Happy coding! 🚀