Querying Channels

If you’re building a similar application to Facebook Messenger or Intercom, you’ll want to show a list of Channels. The Chat API supports MongoDB style queries to make this easy to implement.

You can query channels based on built-in fields as well as any custom field you add to channels. You can find the complete list of supported operators in the query syntax section of the docs.

Multiple filters can be combined, but more complex filters can increase the latency of this API, particularly with custom fields. Reach out to support if you have any doubts about your query filter or logic.

As an example, let’s say that you want to query the last conversations I participated in sorted by last_message_at.

Stream Chat does not run MongoDB on the backend, only a subset of the query options are available.

Here’s an example of how you can query the list of channels:

let controller = chatClient.channelListController(
  query: .init(
    filter: .and([.equal(.type, to: .messaging), .containMembers(userIds: ["thierry"])]),
    sort: [.init(key: .lastMessageAt, isAscending: false)],
    pageSize: 10
  )
)

controller.synchronize { error in
  if let error = error {
    // handle error
    print(error)
  } else {
    // access channels
    print(controller.channels)

    // load more if needed
    controller.loadNextChannels(limit: 10) { error in
      // handle error / access channels
    }
  }
}

Query Parameters

nametypedescriptiondefaultoptional
filtersobjectThe query filters to use. You can query on any of the custom fields you’ve defined on the Channel. You can also filter other built-in channel fields, see next section for reference.{}
sortobject or array of objectsThe sorting used for the channels matching the filters. Sorting is based on field and direction, multiple sorting options can be provided. You can sort based on last_updated, last_message_at,updated_at,created_at,member_count, unread_count or has_unread (unread status). Direction can be ascending (1) or descending (-1)[{last_updated: -1}]
optionsobjectQuery options. See below.{}

Some languages does not guarantee field order in objects or maps. That’s why using array of single key objects is necessary

By default when query channels does not have any filter and it will match all channels on your application. While this might be OK during development, you most likely want to have at least some basic filtering.

At a minimum, the filter should include members: { $in: [userID] }.

The query channels endpoint will only return channels that the user can read, you should make sure that the query uses a filter that includes such logic. For example: messaging channels are readable only to their members, such requirement can be included in the query filter (see below).

Common filters by use-case

Messaging and Team

On messaging and team applications you normally have users added to channels as a member. A good starting point is to use this filter to show the channels the user is participating.

let currentUserChannels = chatClient.channelListController(
  query: .init(
    filter: .containMembers(userIds: [chatClient.currentUserId!])
  )
)

currentUserChannels.synchronize()

// or

let thierryChannels = chatClient.channelListController(
  query: .init(
    filter: .containMembers(userIds: ["thierry"])
  )
)

thierryChannels.synchronize()

Support

On a support chat, you probably want to attach additional information to channels such as the support agent handling the case and other information regarding the status of the support case (ie. open, pending, solved).

let channels = chatClient.channelListController(
  query: .init(
    filter: .and([.equal("agent_id", to: chatClient.currentUserId!), .in("status", values: ["pending", "open", "new"])])
  )
)

channels.synchronize()

Channel Queryable Built-In Fields

To learn more about the supported query operators themselves, please see: Query Syntax Operators.

The following channel fields can be used to filter your query results.

NameTypeDescriptionSupported OperatorsEXAMPLE
frozenbooleanchannel frozen status$eqfalse
typestring or list of stringthe type of channel$in, $eqmessaging
idstring or list of stringthe ID of the channel$in, $eqgeneral
cidstring or list of stringthe full channel ID$in, $eqmessaging:general
membersstring or list of stringthe list of user IDs of the channel members$in, $eqmarcelo or [thierry, marcelo]
invitestring, must be one of these values: (pending, accepted, rejected)the status of the invite$eqpending
joinedbooleanwhether current user is joined the channel or not (through invite or directly)$eqtrue
mutedbooleanwhether the current user has muted the channel$eqtrue
member.user.namestringthe ‘name’ property of a user who is a member of the channel$autocomplete, $eqmarc
created_by_idstringthe id of the user that created the channel$eqmarcelo
hiddenbooleanwhether the current user has hidden the channel$eqfalse
last_message_atstring, must be formatted as an RFC3339 timestampthe time of the last message in the channel$eq, $gt, $lt, $gte, $lte, $exists2021-01-15T09:30:20.45Z
member_countintegerthe number of members in the channel$eq, $gt, $lt, $gte, $lte5
created_atstring, must be formatted as an RFC3339 timestampthe time the channel was created$eq, $gt, $lt, $gte, $lte $exists2021-01-15T09:30:20.45Z
updated_atstring, must be formatted as an RFC3339 timestampthe time the channel was updated$eq, $gt, $lt, $gte, $lte2021-01-15T09:30:20.45Z
teamstringthe team associated with the channel$eqstream
last_updatedstring, must be formatted as an RFC3339 timestampthe time of the last message in the channel. If the channel has no messages, then the time the channel was created$eq, $gt, $lt, $gte, $lte2021-01-15T09:30:20.45Z
DisabledbooleanWhether the channel is disabled or not.$eqfalse
has_unreadbooleanRetrieve channels where the user has an unread message. Only “true” is supported, up to 100 channelstruetrue

Querying by the channel Identifier should be done using the cid field as far as possible to optimize API performance. As the full channel ID, cid is indexed everywhere in Stream database where id is not.

Query Options

nametypedescriptiondefaultoptional
statebooleanif true returns the Channel statetrue
watchbooleanif true listen to changes to this Channel in real time.true
limitintegerThe number of channels to return (max is 30)10
offsetintegerThe offset (max is 1000)0
message_limitintegerHow many messages should be included to each channel (Max 300)25
member_limitintegerHow many members should be included for each channel (Max 100)100

Query channels allows you to retrieve channels and start watching them at same time using the watch parameter set to true.

Response

The result of querying a channel is a list of ChannelState objects which include all the required information to render them without any additional API call.

ChannelState Response

Field NameDescription
channelThe data for this channel
messagesThe most recent messages for this channel (see message_limit option)
watcher_countHow many users are currently watching the channel
readThe read state for the members, up to 100 members, ordered by the most recent added, the current user’s read state is always included
membersThe list of members, up to 100 ordered by the most recent added
pinned_messagesUp to 10 most recent pinned messages

Pagination

Query channel requests can be paginated similar to how you paginate on other calls. Here’s a short example:

let controller = chatClient.channelListController(
  query: .init(
    filter: .containMembers(userIds: ["thierry"]),
    pageSize: 10
  )
)

// Get the first 10 channels
controller.synchronize { error in
  if let error = error {
    // handle error
    print(error)
  } else {
    // Access channels
    print(controller.channels)

    // Get the next 10 channels
    controller.loadNextChannels { error in
      // handle error / access channels
      print(error ?? controller.channels)
    }
  }
}

It is important to note that your filter should include, at the very least {members: {$in: [userID]} or pagination could break.

© Getstream.io, Inc. All Rights Reserved.