Nuxt-Socket.IO: How Namespaces Config May Make Your Life Insanely Easier
Tags: nuxt, vue, javascript, easy
Published: 2019-12-28 05:33:57

TL; DR -- This Christmas present has arrived a little late, but I think users of the [nuxt-socket-io](https://www.npmjs.com/package/nuxt-socket-io) will like it, once it's fully understood. Now, it is possible to configure socket.IO emitters, listeners, and "emitbacks" directly in `nuxt.config` for specified *namespaces*. This article describes how to take advantage of the new feature.

Disclaimer: I am the author of the nuxt-socket-io module. I present here a new kind of syntax which may take some getting used to, but I think it's a syntax that makes sense. Approach it with an open mind. 

---


## Prerequisites:

1. [Introduction to Nuxt Socket.IO](https://medium.com/javascript-in-plain-english/introduction-to-nuxt-socket-io-b78c5322d389) - This describes the Nuxt Socket.IO and basic setup.

## Introduction:

Socket.IO is the real-time engine for the web, and the Nuxt-Socket.IO module is the module that makes it clean and straightforward to use inside your Nuxt application. A lot of times, it is desired to instantiate socket.IO client inside of components, and then restrict the scope of communication to only what those components should and would care about. 

For example, a chat room component might only want to talk to a chat room service, and a room's channel might only want to talk to that channel's service. The messages that get sent in a specific channel shouldn't leak out to the rest of the room. Whether or not we refer to them as "channels" or "rooms", the word "namespaces" seems to be most fitting for each case. Plus, whereas "rooms" and "channels" confine our thinking to that of "just a chat application", the term "namespaces", on the other hand, is universal and allows to think of scoped communication for all of our web applications. 

In addition to scoping a component's IO to that of a given namespace, it is often desirable to also signal a disconnect (i.e., close the socket) when the component is destroyed. While it is good practice for developers to do their own cleanup steps, such practice can either be cumbersome or easy to forget for every component in a given application.

Therefore, with the aforementioned in mind, the nuxt-socket-io plugin and namespace config feature had the following goals:

* The plugin had to allow namespaces to be configured in `nuxt.config` for each socket.
* The plugin had to support the configuration of emitters, listeners, and "emitbacks" at the page- and component-level. 
* The configuration had to be as straightforward as that for vuex options (listeners and emitbacks), for users who already configured the module.
* The new feature had to support a new and, arguably, a more intuitive arrow (`-->`) syntax. This makes IO config easier to version control, and share with stakeholders (non-devs), if needed.
* The configuration had to allow support for hooks to be run before and after the specified IO events. 
* The plugin had to automatically disconnect the socket before the component is destroyed, by default (overwriting this is possible with the `teardown: false` option passed to `this.$nuxtSocket`).
* The plugin had to make life easier for application developers by encouraging them to write less, but more consistent code.
* The plugin still had to expose the socket.io client instance in case the developer needed to access the client API methods directly.
 
Today, it is now possible to configure namespaces in `nuxt.config`. Each socket set can have its own configuration of namespaces and each namespace can now have emitters, listeners, and emitbacks. The configuration supports an arrow syntax in each entry to help describe the flow (with pre/post hook designation support too). 

## Configuring Namespaces

This section describes how to configure emitters, listeners and emitbacks for each namespace. The general syntax is:

> preHook] body [postHook

Sometimes the body includes a "+", "<--" or a "-->". While the use and placement of the characters "]", "[", "+", "<--", and "-->" is strict, the *names* you use for hooks, events, and props are entirely up to you.

The *specific* syntax is as follows:
* **Emitters**: 

> preEmit hook] componentMethod + msg --> componentProp [postRx hook

** How to think about this: the IO event is triggered by `componentMethod`, and the event "componentMethod" gets sent with "msg" (defined in the component as `this.msg`). When the server responds, the response goes to component's prop `componentProp`. `preEmit` hook is run before the event is sent, and `postRx` hook is run after data is received. A nice thing here is the *plugin* creates the `componentMethod` for you so you don't have to. Just call it and it will work.

→ The `preEmit` and `postRx` hooks are optional, but if using them, the "]" and "[" characters are needed so the plugin can parse them. These hooks would be defined in the component's methods section like `this.preEmit` and `this.postRx`
→ The `msg` is optional, but if using, must use the '+' character
→ The `componentMethod` is auto-created by the plugin and sends the event with the same name. If the `componentMethod` is named "getMessage" it sends the event "getMessage"
→ The `componentProp` is optional, but if entered, will be the property that will get set with the response, if a response comes back. This is optional too, and needs to be initially defined on the component, otherwise it won't get set. Vuejs will also complain if you try to render undefined props. If `componentProp` is omitted from the entry, the arrow "-->" can also be omitted.

* **Listeners**: 

> preHook] listenEvent --> componentProp [postRx hook

** How to think about this: when event "listenEvent" is received, the prop `this.componentProp`will be set to that event's data. `preHook` will run when data is received, but before setting `componentProp`. `postRx` hook will run after setting the `componentProp`. 

→ Both `preHook` and `postRx` hooks are optional. Here, `preHook` is called when data is received, but *before* setting componentProp. `postRx` hook is called after setting the prop. `this.preHook` and `this.postRx` would need to be defined in the component's methods section if planning to use either.
→ If using the arrow syntax, when `listenEvent` is received, `componentProp` will get set with that event's data. If only the `listenEvent` is entered, then the plugin will try to set a property on the component of the same name. I.e., if `listenEvent` is "progressRxd", then the plugin will try to set `this.progressRxd` on the component.
→ Important NOTE: This syntax can now also work on the Vuex options for mutations and actions, which are also set up as listeners.

* **Emitbacks**:

> preEmitHook] emitEvt <-- watchProp [postAck hook

** How to think about this: When a component's property `watchProp` changes, emit back the event "emitEvt". `preEmitHook` will run before the data is emitted, and `postAck` will run after the server acknowledges its event, if any.

→ `preEmitHook` and `postAck` hooks are optional. `preEmitHook` runs before emitting the event, `postAck` hook runs after receiving the acknolwedgement, if any. `this.preEmitHook` and `this.postAck` would need to be defined in the component's methods if planning to use.
→ `watchProp` is the property on the component to watch using "myObj.child.grandchild" syntax. Just like you would on the component. 
→ `emitEvt` is the event name to emit back to the server when the `watchProp` changes. If `watchProp` and the arrow "<--" are omitted, then `emitEvt` will double as the `watchProp`. 
→ Important NOTE: this syntax can now also work in the Vuex options for emitbacks, with ONE important difference. In Vuex (and Nuxt, specifically), the watch property path may require forward slashes "/". For example, if your stores folder has an "examples.js" file in it, with state properties "sample" and "sample2", watchProp would have to be specified as "examples/sample" and "examples/sample2". The exception to the rule is "index.js" which is treated as the stores root. I.e., "sample" in index.js would be referred to simply as "sample" and not "index/sample")

---

## Example Configuration

Consider the following configuration as an example:
In `nuxt.config.js`:
```
namespaces: {
  '/index': {
    emitters: ['getMessage2 + testMsg --> message2Rxd'],
    listeners: ['chatMessage2', 'chatMessage3 --> message3Rxd']
  },
  '/examples': {
    emitBacks: ['sample3', 'sample4 <-- myObj.sample4'],
    emitters: [
      'reset] getProgress + refreshInfo --> progress [handleDone'
    ],
    listeners: ['progress']
  }
}
```

1) First, let's analyze the `/index` config. 

* Emitters:
When `getMessage()` is called, the event "getMessage" will be sent with component's data `this.testMsg`. `this.testMsg` should be defined on the component, but if it isn't no message will get sent (the plugin will warn when the component data is not defined). When a response is received, `this.messageRxd` on the component will get set to that response.

* Listeners: 
When `chatMessage2` is received, `this.chatMessage2` on the component will be set. When `chatMessage3` is received, the mapped property `this.message3Rxd` will be set.


2) Let's analyze the `/examples` config.

* Emitbacks: 
When `this.sample3` changes in the component, the event `sample3` will be emitted back to the server. When `this.myObj.sample4` changes in the component, the mapped event `sample4` will be emitted back.

* Emitters:
When `this.getProgress()` is called, *first* `this.reset()` will be called (if it's defined) and *then* the event "getProgress" will be emitted with the message `this.refreshInfo`. When the response is received, `this.progress` will get set to the response, and then `this.handleDone()` will be called (if it's defined)

* Listeners:
When event "progress" is received, `this.progress` will get set to that data.

## Chat rooms: a more exciting example

Want to see a slightly more exciting example? Check out my [very basic] chat rooms example!

1. Clone my git repo: https://github.com/richardeschloss/nuxt-socket-io
2. Run the server with `npm run dev:server`
3. Go to the chat rooms page at: https://localhost:3000/rooms and have fun!
(open two browser windows because...you need at least two clients to have a conversation; you can also chat with yourself, if you want :))

My next article in this series should help explain that example.

## Conclusion

This was a rather lengthy, but important, discussion of nuxt-socket-io's new namespace configuration feature. While the time spent reading this article may have been longer than desired, it is very possible that the time spent on your future web applications will be significantly reduced by taking advantage of what you learned here. It may be a bumpy road at first, but I think over time, you'll get used to the syntax. If you hate it, no worries...the plugin will still expose the socket.io client API directly, so your fingertips will have that if needed.

## Credits / Acknowledgement

Thanks to Ofoe Apronti [@OfoeApronti](https://github.com/OfoeApronti) for opening the issue and asking about it. At first, I didn't think there was a clean way to create this feature, and gave up on it early. But, with more thought put into it, I think the solution may end up satisfying most users (fingers crossed).