From REST to NATS: How Codiac created a dual-headed API framework for seamless migration
In this talk, Mark Freydl of Codiac, a DevOps platform, walks through how Codiac chose to implement NATS in the their platform. Why? To overcome limitations with REST APIs, particularly for long-running processes and messaging.
This transition unlocks several benefits for them, including better flexibility in workload placement (local machine, cloud, or customer clusters), improved security posture, more efficient caching capabilities, and reduced server load. They developed a dual-headed API framework that supports both HTTP and NATS simultaneously, allowing for gradual migration without disrupting their services or customers.
"But we're running out in the cloud, in our cloud. We're also running on the local machine, and we're also running up in the clusters of our customers.
And depending on who it is, NATS gives us the capability to say, well, where do you want the workhorse? Do you want the workhorse over at the SaaS product? Do want the workhorse on the local machine? Or do want the workhorse up in the cluster?
Suddenly, you guys offer a really great opportunity to really rearchitect that and maybe even make it flexible"
— Mark Freydl, Co-CEO, Codiac on what NATS unlocks
Go Deeper
Full Transcript
Mark Freydl:
Hey, everyone. I'm Mark Freydl. I'm with Codiac, and Codiac is a DevOps platform. And for a lot of reasons that everybody on this call I'm sure would agree with, we're making a full changeover to NATS, all of our communications over to using NATS. And that's not a small task.
One of the challenges that we faced in doing that was the fact that we're running a lot of REST APIs. And this is probably a pretty common problem for a lot of people and saying like, well, Okay, I don't have just the one. We have several different APIs and we have several different services relying on them. Really the question moving to NATS became, well, if there's customers out there using the system and we're using the system and we've got a number of different environments and we have a number of different code bases, everything's moving, how do you change the tires on a moving car? Do we create a completely separate code base for the APIs and then one day do a big massive transition?
Or do we try to set everybody in some sort of way and allow them to do it individually. Well, we've got backward compatibility to deal with. We have all these various things to contend with. And so really none of those other options sounded very good. We have our own API system, and we upgraded it to support both.
So it supports HTTP and NATS. And so we named it Orthrus, the two headed dog, now the two headed API framework. Anyway, having both of these allows us a lot of flexibility in this changeover that we're doing. Let me show you where we landed with that. So this is our CLI.
I'm going to run a small command. I told you we're a DevOps company. I'm going to list out the different cabinets or environments that we have here. And these are for a certain enterprise. There's internal environments.
And these are different environments that we show here, the production, the dark environment. Then we have this config file over here. And this config file is telling us, Okay, which client do you want to use? And right now, I just showed you that we were using HTTP for that. Let's switch over.
And now we can run the exact same thing against the exact same API with a different client, and this time using the NATS client. Now you'll have to believe me that that was actually a NATS client. We'll step through that in a minute. But I want to tell you a little bit about what went on there. We generated clients, one for each, one for an API client, and one for the NATS client.
And the key to it was that those clients were actually using both exactly the same contracts. They're both exactly Codiac clients, so they're interchangeable. Meanwhile, the API out there is running both at the same time. We were able to do this generation using the code using the OpenAPI spec. So the API itself generates an OpenAPI spec, a swagger specification, if you've heard of that that way.
And those are written for REST clients. But we also found that there's also extensions. And so with those extensions, we can write a little NATS extension that allows us to put NATS subjects in there. Now, we can be conventional about how the way we did these paths and said, well, let's use the HTTP verb, which you see here at post activities, and say HTTP verb dot whatever path was and exchange the slashes for dots. But at some point, we just need to make that transition in the subjects.
But this allows these two to drift apart and do it very gradually and not interrupt each other because they're both using different clients. So anyway, back to our little demo here. This time, why don't we step through the code? And I'll show you way that transition was happening and how we pull that off. So this is, again, our little cab list.
But this time, because this config variable is coming in here being brought in here this way, we can take that variable based on what that is and decide using our IOC container inversion of control. We can say, this is what a Codiac client is going to be. We're going to use the NATS bind the NATS client for it. And away we go. Later, when the request is coming in and the NATS client is connecting, we have these tokens, this Codiac JWT token.
This is from our legacy auth service or existing auth service. And what we decided to do is to say, let's make those tokens portable from REST to NATS. They're both being used against the same API. But because we're using the auth callout, we're going to be able to put our NATS creds together in line as it connects because there's a inside this token, the token payload, there's this NATS token. And that NATS token is the one that we use to create the NATS authenticator down here, which allows that NATS client to run and do its works, which is really nice.
So again, back here one more time here, run it through HTTP because the CLI just reboots itself every time it has to run a command. We'll see now we can run that exact same request. And this time, we're running it not through NATS but through HTTP. And boom, same stuff. So the nice part about this is that auth callout allows us backward compatibility with our existing auth.
It allowed us the dual headed API framework that allows those APIs to sit out there and say, well, we may have 10 or 15 different services that are accessing them that can change over on their own time. And they can decide how they're going to do that. That ability to do it polymorphically, like I just showed you with those API clients that we generated using the open API spec, that was really super handy for us. Anyway, let's in general, we were handling a dual transport, NAS and HTTP. We used the OpenAPI spec, and we generated clients from it.
We also extended it to allow for the NAS subject to be put in there. And then we brought those clients in using inversion of control and polymorphism to be able to switch them out and do that on a configurable basis. That auth callout we were using allowed us to bring that legacy auth into play, and we can then change it out later. But the point was, let's get on to NATS and make those decisions later. And this was a really smooth, easy way to do it.
And then that really allowed us to say, ultimately, we're moving to a leaf hub topography. But we need to be on NATS before we can do that. And this is a perfect initial step, and it allows us to do it gradually and safely. One more thing, that API framework that we're using, I want to open source it. We're in the DevOps platform business.
We're not in the API business. But now that we're running NATS, there's a lot of new variables on the table. And really, if any of you out there want to help out with this or help me open source this, I would love to do that. My email is down there and I'd love to hear from you. Thanks a lot.
I'm Mark Freydl. I'm with Codiac.
Nate Emerson:
We've got Mark Freydl joining us for a live Q and A.
Mark Freydl:
How's it going, guys?
Nate Emerson:
Thank you so much for the time. And now we had one question right off the bat that came in. So what was the motivation for incorporating NATS into your DevOps platform, and do your customers feel the difference?
Mark Freydl:
Well, our customers have only seen a little bit of it so far, because it's a big job, but the the motivation, though, is an interesting one. I think I heard it, mentioned a couple times earlier today on the call. It started off small. We're again, we're in the DevOps business. We're running a lot of IAC packages.
We're running a lot of, long running, processes. So the first the idea was, well, REST is a little clumsy for that. And, we came up with some, you know, potentially elegant solutions, looked elegant at the time for trying to figure out how to get the job done, the job triggered, the job watched, and the job notified, and the client notified. Anyway, obviously, the ability to put NATS into play a better messaging system a little bit smoother transition there made a lot of sense. But then you start thinking about caching.
You start looking at Jetstream, and you start looking at key value store. We have a lot of data that doesn't really move that quickly. The list of environments that you have, though you can change them very accessibly in Codiac, that's wonderful, but you're not going be doing that every few seconds or anything. It's really not a lot of data. But we're running out in the cloud, in our cloud.
We're also running on the local machine, and we're also running up in the clusters of our customers. And depending on who it is, NATS gives us the capability to say, well, where do you want the workhorse? Do you want the workhorse over at the SaaS product? Do want the workhorse on the local machine? Or do want the workhorse up in the cluster?
Suddenly, you guys offer a really great opportunity to really re architect that and maybe even make it flexible. So that was a big part of it, too. There was also a hugely different security footing we can put our product on. Allowing people in or out of the ecosystem itself is a big deal. The ability to say, I have a request that is happening a lot, and we can cache that request, but I also need to push data down to get that client on the latest instance of whatever we have.
That was really important. We can deload our servers with this really heavily, knowing that bad requests just simply aren't gonna make it upstream, period. It's phenomenal. So I think a lot of people, you start on one thing, and the idea starts to grow. Now we just know it.
When we're going bring up the topic in discussions, we know, well, can we do that with NATS? It's almost like a cliche at this point.
Nate Emerson:
Yeah. I think you spoke to it a little bit, but, yeah, I was just gonna follow-up with kind of the team's internal experience and how they've enjoyed integrating it and leveraging it as a messaging platform. But it sounds like it's been yeah.
Mark Freydl:
Yeah. Well, I mean, it's been it's been a challenge for one. You know, that that API changeover was a was a tough call because, I mean, if we're running on our own on our own API framework, it was like, look, I'd rather use something that was supported by the community rather than us. And the world doesn't need another REST framework. Does the world need another NATS framework, though? I mean, that's a whole different story, right?
So we're sort of posing that idea. But we're about one step in the middle of a pretty deep plan, and we're still pretty much swimming in the shallow end of the pool. I mean, I love hearing all the great stuff that people are doing with this. It's just really fun conversations to have. But, you know, sort of like your dad kinda walks in and says, alright.
Well, let's talk about what's right now. You know, we've got plenty to think about.
Nate Emerson:
Well, thank you so much for joining us. Unfortunately, we do have to stay on schedule. We've got a few more things to get through, but thank you, Mark, for the time.