Users, channels, and memberships in DataSync
Users, channels, and memberships are DataSync's built-in classes: a user and a channel are entities, and a membership is a relationship linking a channel to a user. Everything on the Data model page applies to them too, including payloads, ETags, TTL, events, and access control.
Users
A user is an entity of the built-in Global User entity class, which PubNub seeds at version 1. DataSync picks the class for you when you don't name one. Creating a user requires only entityClassVersion. The id is optional, and DataSync generates one if you omit it, as are status and payload.
The user endpoints are Get users, Get user, Create user, Update user, Patch user, and Delete user.
A user's fields:
| Field | Purpose |
|---|---|
id | The user ID |
createdAt | Creation timestamp |
updatedAt | Last modification timestamp |
eTag | Optimistic concurrency marker |
status | A free-form application lifecycle string, 1 to 100 characters |
expiresAt | Expiry timestamp. An instance of the Global class inherits its 30-day TTL |
entityClass | The name of the entity class, User unless you've extended it |
entityClassVersion | The version of the User class this instance pins to |
entityClassLevel | Whether that class is Global or SubKey (keyset-level) |
payload | Your application data |
The Global User class itself can't be changed. To add your own fields, use Create a new entity class to define a class on your keyset that extends the Global User class as its parent, through an extends reference (see Class inheritance).
The subclass name is up to you, it does not need to match the parent's. You create, read, and list instances of your extended class, the same as any other Global User, and they validate and project against your extended definition while still being users in every other sense (the same users resource type for Access Manager grants, the same fields table above).
Your subclass can't itself be extended. A class can only extend a class defined at a higher level, so a keyset-level class extends a Global class and the hierarchy stops there.
In Bob's marketplace, Bob defines a keyset-level MarketplaceUser class at version 1, extending the Global User class, adding display_name, avatar_url, email, and phone. user-alice is a user entity created against that version, with a payload of
{ "name": "Alice", "type": "shopper", "display_name": "Alice", "avatar_url": "https://.../alice.png", "email": "alice@example.com", "phone": "+1-555-0100" }
You can't modify the Global class, so you extend it. A subclass inherits its parent's properties automatically: MarketplaceUser declares only Bob's four fields, and inherits name (/payload/name, string, nullable) and type (/payload/type, string, nullable) from the parent, for six effective properties in all. If you do redeclare an inherited property yourself, its path, value kind, and nullability must match the parent exactly, or the class is rejected, but you're free to pick a different filtering mode or a different set of projections for an inherited property.
Channels
A channel is an entity of the built-in Global Channel entity class, with the same shape as a user. Refer to the fields table above for the channel's fields, they are the same as a user's.
A channel entity stores data about a channel your app uses for messaging, for example a display name or a category.
The channel endpoints are Get channels, Get channel, Create channel, Update channel, Patch channel, and Delete channel.
A channel entity in DataSync isn't a pub/sub channel
Creating a channel entity doesn't create or configure the underlying Pub/Sub channel, and a channel does not need an entity for messaging to work.
The entity is where the channel's metadata lives.
The channel entity holds metadata, while the Pub/Sub and Presence channel carries messages and subscribers. They share an ID but have separate lifecycles, and messaging works whether or not the entity exists.
In Bob's marketplace, channel-summer-sale is a channel entity with a payload of
{ "name": "Summer Sale Live", "type": "live-stream" }
Memberships
A membership is a relationship of the built-in Global Membership relationship class, linking a channel and a user. The Membership class is many-to-many, so a user can belong to many channels and a channel can have many members. Its two sides are pinned to the Global classes: entity A must be a Channel and entity B must be a User, or a subclass of either.
The membership endpoints are Get memberships, Get membership, Create membership, Update membership, Patch membership, and Delete membership.
A membership's fields, in addition to the system fields shared with entities:
| Field | Purpose |
|---|---|
id | The membership's own identifier, and the only handle for get, replace, partial update, and delete. There's no addressing a membership by its channel and user pair |
channelId | The linked channel's ID, the relationship's entity A |
userId | The linked user's ID, the relationship's entity B |
relationshipClass | Always Membership, set by DataSync. Relationship classes can't be extended |
relationshipClassVersion | The version of the Membership class this instance pins to |
payload | Association data, for example a role |
A membership is a relationship, so it follows every relationship rule on the Data model page, with a few specializations:
- The sides are renamed. Where a generic relationship exposes
entityAIdandentityBId, a membership exposes them aschannelIdanduserId. relationshipClassis fixed toMembershipand can't be supplied.- The channel must be a
Channel, or a subclass of it, and the user must be aUser, or a subclass of it, or the request is rejected with a400. expiresAtis derived as the earlier of the channel's and the user's expiry, and can't be set.- DataSync events keep the generic
entityAIdandentityBIdnames.
Creating a membership requires channelId, userId, and relationshipClassVersion. The id is optional, as are status and payload. Both the channel and the user must already exist, or the request returns a 404. Only one membership can exist for a given channel and user, so a duplicate returns a 409.
Memberships support both replace (PUT) and partial update (PATCH), the same as any other relationship. Refer to Data operations for more information.
In Bob's marketplace, Alice's membership in channel-summer-sale carries a payload of
{ "role": "viewer" }
Because the Membership class declares no properties and can't be extended, membership payload fields are never indexed. You can't filter or sort memberships by role. The built-in fields id, createdAt, updatedAt, and status still work, so you can order memberships by when they were created even though the payload isn't searchable.
Membership lists can be narrowed by user_id, by channel_id, or by both together, which covers the two common questions: which channels a user belongs to, and who is a member of a channel. Both parameters are optional, so omit them to list every membership. Refer to Get memberships.
Real-time updates
Creating, updating, or deleting a user, channel, or membership can publish a DataSync event, the same way any other entity or relationship can. Events are off by default and are enabled per class version, per keyset, in the Admin Portal. A create event for user-alice is published on the user-alice channel. Update and delete events are also published on the channels of every entity user-alice is linked to. A membership event is published on both the linked channel's and the linked user's channels, never on the membership's own ID. Refer to Events for more information.
Because a membership event lands on the linked user's and channel's ID channels, the way to watch Alice's memberships appear and disappear is to subscribe to user-alice, not to any membership id. SDKs that ship DataSync SDK entities expose that as a user handle and a channel handle. A membership handle exists for symmetry, but it receives nothing for the link itself.
Creating user-alice against Bob's MarketplaceUser class, then reading her back by id:
- JavaScript
- C#
1const created = await pubnub.dataSync.createUser({
2 id: 'user-alice',
3 class: 'MarketplaceUser',
4 data: {
5 classVersion: 1,
6 payload: {
7 name: 'Alice',
8 type: 'shopper',
9 display_name: 'Alice',
10 avatar_url: 'https://.../alice.png',
11 email: 'alice@example.com',
12 phone: '+1-555-0100',
13 },
14 },
15})
show all 17 lines1PNResult<PNDataSyncUserResult> created = await pubnub.DataSync.CreateUser(new CreateUserParameters
2{
3 Id = "user-alice",
4 EntityClass = "MarketplaceUser",
5 EntityClassVersion = 1,
6 Payload = new Dictionary<string, object>
7 {
8 { "name", "Alice" },
9 { "type", "shopper" },
10 { "display_name", "Alice" },
11 { "avatar_url", "https://.../alice.png" },
12 { "email", "alice@example.com" },
13 { "phone", "+1-555-0100" },
14 },
15});
show all 20 linesRefer to Create user (JavaScript), Create user (C#), and Create user (REST) for the full parameter reference, including the equivalent methods for channels and memberships.