There are definite advantages to using Channel Groups when you need to subscribe to many channels. Channel groups allow you to subscribe to 2000 channels at once by subscribing to the a channel group that has those channels added to it. Clients can also subscribe to a maximum of 10 channel groups for a total of 20K channels! 


Your clients may not need to listen to that many channels but channel groups make it possible for your server to manage the channels that the client is subscribed to by adding and removing channels on behalf of the clients. This provides additional security among other benefits but there are a few things you might want to implement to add more value to your channel groups implementation.


When your client subscribes to a channel group, it must already contain a channel or the client will receive a 400 Bad Request error response. So before your client subscribes to a channel group, be sure to add a default channel that never gets removed. This will not only prevent the 400 empty group error scenario but the channel can also serve as a channel group events feature. "What kind of events", you ask? Well, anything you desire but for starters, it may be very beneficial, or necessary, for you client app to know when a channel is added or removed, or when the channel group is full (has 2000 channels) or empty (only has the default channel in it - remember, let's avoid getting that 400 response). Now, this doesn't mean that your end user has to know when these actions happen, but your app may need to take some action under the hood.


Since your server is the entity that must do the channel adds and removes (manage permission should never be given to a client app), it will be simple to bundle all this logic in one place to fire these events on this default channel group channel that you added. 


function createChannelGroup(channelGroup) {
    addChannels(channelGroup, [channelGroup + "-events"]);
};

function destroyChannelGroup(channelGroup) {
    // notify subscribers of the channel group so they can take 
    // any necessary actions, like unsubsribing from the channel group
    // or other cleanup that might be required
    pubnub.publish(
        {
            "channels": [channelGroup + "-events"],
            "message": { 
                "action": "deleteGroup"
            }
        }, 
        function (status, response) 
        {
            if (status.error) {
                // handle error
            } 
            else {
                pubnub.channelGroups.deleteGroup(
                    {
                        "channelGroup": channelGroup
                    },
                    function (status) {
                        if (status.error) {
                            // handle error
                        } 
                        else {
                            // handle
                        }
                    }
                );
            }
        }
    );
};

function addChannels(channelGroup, channels) {
    pubnub.channelGroups.addChannels(
        {
            "channels": channels,
            "channelGroup": channelGroup    
        },
        function(status) {
            if (status.error) {
                // handle error
                // if channel group channel limit was hit
                // then we need to publish that event
                // or just notify the "adder" 
            } 
            else {
                // no event for the "-events" channel add
                // because no one should be listening yet
                if (channelName == (channelGroup + "-events") return;

                pubnub.publish(
                    {
                        "channel": channelGroup + "-events",
                        "message": { 
                            "action": "add",
                            "channels": channels
                        }
                    }, 
                    function (status, response) 
                    {
                        if (status.error) {
                            // handle error
                        } 
                        else {
                            // handle success
                        }
                    }
                );
            }
        }
    );
};

function removeChannels(channelGroup, channels) {
    pubnub.channelGroups.removeChannels(
        {
            "channels": channels,
            "channelGroup": channelGroup    
        },
        function(status) {
            if (status.error) {
                // handle error
                // if channel group has no 1 channel left (-events)
                // then we need to publish the "empty" event
                // or just notify the "remover" 
            } 
            else {
                // no event for the "-events" channel remove
                // because no one should be listening anymore
                if (channelName == (channelGroup + "-events") return;

                pubnub.publish(
                    {
                        "channel": channelGroup + "-events",
                        "message": { 
                            "action": "remove",
                            "channels": channels
                        }
                    }, 
                    function (status, response) 
                    {
                        if (status.error) {
                            // handle error
                        } 
                        else {
                            // handle success
                        }
                    }
                );
            }
        }
    );
};