logo
Published on

NATS, more productivity for client side development

Authors

NATS is a simple, secure and high performance open source messaging system for cloud native applications, IoT messaging, and microservices architectures. NATS has the following characteristics:

  • Simple: NATS is easy to understand, install, and use.
  • Secure: NATS supports authentication, authorization, and encryption.
  • High Performance: NATS is designed to be fast, scalable, and reliable.
  • Cloud Native: NATS is cloud native and can run anywhere.

Traditionally, NATS runs in a server environemnt, where it help connecting multiple backend services as an orchestrator or service bus, while front end / mobile applications are kept away and still connects to mutiple endpoints/APIs in order to integrate with the system backend. IMHO, this is a huge missed opportunity.

The relationship between the server and the client usually designed like this:

and in reality the implementation looks actually like this:

What about if we change it to this:

Thanks to NATS built in support for WebSockets and client libraries like NATS.WS, now we can have the client web or mobile application to join the server environemnt as a first class citizen and interact directly with backends services via NATS!! This will basically get rid of ALL API endpoints that is being usually built just to serve frontend traffic.

Basically, using NATS client libraries like NATS.WS, the client (web or mobile app) can:

  • Request any date synchronously like Triditional REST API via NATS Request/Reply and get a response WITHOUT the need to know which service is handling the request or main the address of this service !!
  • Do server stuff like subscribing to a topic and get notified when a new message is published to this topic (this is live update from UX prespective).
  • Send Super fast asynchronous data to the server if knowing the response is not required, like event tracking or logging.
  • Send messages to other clients (like chat app) which usually requires custom implementation but with NATS it's basically an added bonus.

All the above is nice, however, in my opinion, the main benifit here is the boost in developer porductivity due to the reduced complexity of the client side code, let's say the client app is doing CRUD for 5 objects, with REST API, that's 20 endpoints to implement, maintain, version .. etc, with NATS, the entire client side app will just interact with the actual backend services directly by requesting a topic and sending a payload and whatever servie subscribing to this topic will handle and reply to the request :)

Another byproduct is a nice gain in performance, HTTP by design is stateless, means that each signle request will need to go through the entire server stack, AuthN, AuthZ, http headers .. etc.

with NATS, authentication can happen once and the client will stay connected and will be able to send as many requests as it needs with the same security context.

Here is a comparison in performance between HTTP and NATS using 2 GO APIs, for Rest & NATS, that display same number of records from MongoDB AirBNB Sample Dataset:

and here is a code sample for generic react custom hook to handle all the requests:

export const useNATS = () => {
  const [nats, setNATS] = useState(null)
  const [natsConnected, setNATSConnected] = useState(false)
  const [natsError, setNATSError] = useState(null)

  useEffect(() => {
    const connectNATS = async () => {
      try {
        console.log('connecting to nats', natsUrl)
        const nc = await connect({
          servers: 'ws://natsserver:4222',
        })

        setNATS(nc)
        setNATSConnected(true)
        console.log('NATS connected')
      } catch (error) {
        setNATSError(error)
        console.log('NATS error', error)
      }
    }
    connectNATS()
  }, [publicRuntimeConfig, natsUrl])

  // request data synchronously
  async function request(topic, data) {
    const jc = JSONCodec()
    const res = await nats.request(topic, jc.encode(data))
    return jc.decode(res.data)
  }

  // publish data asynchronously
  async function publish(topic, data) {
    const jc = JSONCodec()
    nats.publish(topic, jc.encode(data))
  }

  return { nats, natsConnected, natsError, request, publish }
}