Over a million developers have joined DZone.

REST to NATS Proxy

DZone's Guide to

REST to NATS Proxy

As microservices architectures are increasingly optimized for speed and simplicity, developers are increasingly using NATS—a lightweight high-performance, cloud-native messaging system—for inter-service communication. An interesting project developed in the NATS community bridges HTTP and NATS, and is explained in this article by Radomir Sohlic.

· Integration Zone ·
Free Resource

WSO2 is the only open source vendor to be named a leader in The Forrester Wave™: API Management Solutions, Q4 2018 Report. Download the report now or try out our product for free.

The REST to NATS proxy project (sohlich/nats-proxy) is the micro framework that provides a bridge between HTTP and NATS. To introduce the problem, we first compare the HTTP and NATS communication models. The table below represents the matching of HTTP and NATS concepts and what do they provide.

synchronous communication Request/Response Request/Reply
real-time asynchronous communication Websocket Publish/Subscribe

As you can see, NATS provides both synchronous and asynchronous communication between clients. The synchronous communication, represented by simple Request and Response of HTTP protocol, could be matched with the Request/Reply communication model of NATS. As the documentation for “request reply” model describes: each request sent via NATS contains reply subject, to which the reply is sent. The asynchronous, let’s say real-time communication, can be represented by Websockets on HTTP side.The truth is that it is not really related to HTTP, but if we simplify it, at least the handshake is based on HTTP. For this purpose, the Publish/Subscribe model could be used.

So the REST to NATS project uses this similarity between NATS and HTTP communication and tries to implement the bridge between HTTP(Websockets) and NATS in such way. The library was originally created for the purpose of migrating REST based architecture like this:

...into a NATS messaging platform-based one. But as it evolved, it started to grow into some kind of framework, that can be used for the creation of service API and seamless protocol bridging. So one of the many examples of how the system using a nats-proxy framework could look like is in the architecture below:

Proxy Basics

As the name of the project suggests the function of the library is very similar to basic HTTP proxy. The proxy receives the HTTP request and translates it into a struct, which contains all the information from original request (URL, header, body).

type Request struct {
URL        string
Method     string
Header     http.Header
Form       url.Values
RemoteAddr string
Body       []byte

This struct is serialized and sent as a message through NATS via request (see http://nats.io/documentation/concepts/nats-req-rep/) to ensure synchronous processing. The subject, to which the serialized struct is sent, is constructed from the HTTP request URL and METHOD by a very simple rule: slashes in the path are replaced by dots and the method is used as the prefix.

  • Let's say we have GET request on URL http://example.com/user/info so the proxy will translate this URL to subject GET:user.info.

The client side is subscribed to the subject GET:user.info. Because of that, it receives the request and writes back the response to the reply subject. The response struct also contains the body, status, and header.

type Response struct {
Header     http.Header
StatusCode int
Body       []byte

For a better picture of how it works in reality, there is the code of a simple client and proxy.


The proxy side implements the http.Handler interface, so it can be used with a built-in http package as you can see in the code below. The handler does nothing special. It parses the request and translates it to custom representation which is then serialized to JSON by built-in json package encoder.


func main() {
proxyConn, _ := nats.Connect(nats.DefaultURL)
proxy, _ := natsproxy.NewNatsProxy(proxyConn)
defer proxyConn.Close()
http.ListenAndServe(":8080", proxy)

The proxy itself does not implement any mechanisms to apply filters before the request is passed to the proxy handler as this could be implemented by decorating the proxy handler or other similar techniques. Because the implementation does not allow writing data to the http.ResponseWriter after the handler is applied, the proxy provides natsproxy.Hook interface. This hook is applied on the response before it is written to http.ResponseWriter. The example bellow shows the usage of hook to translate JWT token with all user info to meaningless reference token.

proxyHandler.AddHook(".*", func(r *Response) {
        // Exchange the jwt token for
        // reference token to hide user information
        jwt := r.GetHeader().Get("X-Auth")
        refToken := auth.GetTokenFor(jwt)
r.GetHeader().Set("X-Auth", refToken)


The client code uses the nats connection as the constructor argument, so all available options for configuring the connection are accessible. The client itself uses the asynchronous subscription to handle incoming messages, so it’s behavior similar to http.HandlerFunc. The client API and internals are heavily inspired by Gin Gonic project. The sample code shows how to use the client API.


func main(){
clientConn, _ := nats.Connect(nats.DefaultURL)
natsClient, _ := natsproxy.NewNatsClient(clientConn)
//Subscribe to URL /user/info
natsClient.GET("/user/info", func(c *natsproxy.Context) {
   user := struct {
    Name string
c.JSON(200, user)
defer clientConn.Close()

// Waiting for signal to close the client
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
fmt.Println("Press Ctrl+C for exit.")

The client API naturally provides the subscription for other HTTP methods and generic subscription method. The request handler which implements natsproxy.NatsHandler interface uses natsproxy.Context struct which encapsulates both Request and Response and provides some useful methods to access request data and to write a response.

natsClient.GET("/user/info", func(c *natsproxy.Context) {
c.JSON(200, "Hello")

natsClient.POST("/user/info", func(c *natsproxy.Context) {
c.JSON(200, "Hello")

natsClient.PUT("/user/info", func(c *natsproxy.Context) {
c.JSON(200, "Hello")

natsClient.DELETE("/user/info", func(c *natsproxy.Context) {
c.JSON(200, "Hello")

//General method
natsClient.Subscribe("HEAD","/user/info", func(c *natsproxy.Context) {
c.JSON(200, "Hello")

The client also implements middleware function that provides the means of accessing the request before it is handled by the specific handler. The reason behind this feature is to provide options for security checks, logging, etc. The example shows the implementation of middleware, that logs all incoming requests.

natsClient.Use(func logger(c *natsproxy.Context) {
    log.Infof("%s:%s from %s", c.Request.Method, c.Request.URL)


The first version (v1) of the NATS-proxy framework implements only HTTP Request/Response proxying. Because it started as a quick proof of concept, there is some additional optimization work needed, and all the work is done on the proxy side. Also, the serialization of structs is done by JSON encoder, which does not provide very fast serialization. However for the purpose of bridging REST (HTTP) requests to NATS messaging platform, it’s enough to make it possible.

Currently, the next version (v2) is under development. The v2 should bring some performance improvements because the serialization is done via protocol buffers. Also, a lot of work originally done on the proxy side was moved to client(service) side. The next significant feature coming is WebSocket support.

If you are interested or have some ideas, see the REST to NATS proxy project and file an issue.

IAM is now more than a security project. It’s an enabler for an integration agile enterprise. If you’re currently evaluating an identity solution or exploring IAM, join this webinar.

microservices ,http ,rest ,golang ,integration architecture ,messaging protocol ,messaging ,messaging middleware

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}