Patterns
Common patterns for pagination, error handling, and real-world usage
channelMessages supports cursor-based pagination using fromOrder and
around. Messages are ordered by a sort key — pass the last message’s order
value to load the next page.
import { setup, query } from '@start-chat/sdk'
await setup({ apiKey: 'sk_live_your_api_key' })
const SERVER = 'srv_...'
const CHANNEL = 'ch_...'
let messages = await query.channelMessages({
serverId: SERVER,
channelId: CHANNEL,
limit: 20,
})
if (messages.length > 0) {
const oldest = messages[messages.length - 1]
const olderMessages = await query.channelMessages({
serverId: SERVER,
channelId: CHANNEL,
fromOrder: oldest.order,
around: 'before',
limit: 20,
})
}
Filtering
Filter messages by type using channelFilter:
const threads = await query.channelMessages({
serverId: SERVER,
channelId: CHANNEL,
channelFilter: 'thread',
})
const pinned = await query.channelMessages({
serverId: SERVER,
channelId: CHANNEL,
channelFilter: 'pinned',
})
const appMessages = await query.channelMessages({
serverId: SERVER,
channelId: CHANNEL,
channelFilter: 'app',
})
Error handling
Setup errors
try {
await setup({ apiKey: 'sk_live_your_api_key' })
} catch (e) {
if (e.message === 'Invalid API key') {
}
}
Query errors
Queries throw on permission or not-found errors:
try {
const channel = await query.channelById({ channelId: 'ch_doesntexist' })
} catch (e) {
}
Mutation errors
Mutations validate required fields before sending:
try {
await mutate.message.send({ channelId: 'ch_...', content: '' })
} catch (e) {
}
Typing indicators
Show when your bot is “typing” before sending a response:
import { setup, mutate } from '@start-chat/sdk'
await setup({ apiKey: 'sk_live_your_api_key' })
const indicator = await mutate.indicator.insert({
serverId: 'srv_...',
channelId: 'ch_...',
type: 'typing',
})
await someSlowOperation()
await mutate.message.send({
channelId: 'ch_...',
content: 'Here is the result!',
})
await mutate.indicator.delete({ id: indicator.id })
Threads
Create a thread and reply to it:
await mutate.thread.insert({
channelId: 'ch_...',
serverId: 'srv_...',
creatorId: 'usr_...',
title: 'Discussion about feature X',
})
await mutate.message.send({
channelId: 'ch_...',
threadId: 'thr_...',
content: 'First reply in the thread',
})
const thread = await query.threadWithMessages({
threadId: 'thr_...',
serverId: 'srv_...',
})
Reactions
await mutate.reaction.insert({
messageId: 'msg_...',
content: '👍',
})
await mutate.reaction.delete({ id: 'reaction_id' })
Pins
await mutate.pin.insert({
messageId: 'msg_...',
channelId: 'ch_...',
})
const pinned = await query.channelMessages({
serverId: 'srv_...',
channelId: 'ch_...',
channelFilter: 'pinned',
})
Local development
Use sk_dev with a local server to skip API key setup:
await setup({
apiKey: 'sk_dev',
baseUrl: 'http://localhost:7878',
})
const servers = await query.userServers()
This auto-authenticates as the admin user. Use it for local testing — never in
production.