 Hello Java developers, my name is Matt Rablin. Today I'd like to show you how to build a reactive microservices architecture using Spring Web Flux and Spring Cloud Gateway. Let's get started. First thing I'm going to do is pull up a blog post that I wrote called Secure Reactive Microservices with Spring Cloud Gateway. So if you want to follow along with this blog post, it has all the code that I'll be using today, and you can use it to copy and paste and create everything yourself. So it links to a GitHub repo. And in this GitHub repo, there's three different styles of microservices. They all use Spring Boot and Spring Cloud. It's just one uses bare-bones Spring Boot and Spring Cloud. One uses J-Hipster, and then one uses Spring Web Flux and Spring Cloud Gateway. So that's one we're going to be doing. I have this demo.adoc, which is just a condensed version of the blog post with simply the steps that we need to do to create everything that's shown in the blog post, and it's kind of a smaller abbreviated version. So I've written in an ASCII doctor, and while I'm doing this demo, what you'll notice is sometimes I just type a few characters, and all of a sudden a whole bunch of code erupts on the screen. And that's because of IntelliJ Live Templates. So I want to point that out right here. And all of my live templates are available in my GitHub repo, mrable, IntelliJ Live Templates, and you can see how to import them in your projects using this readme. So back to the instructions. I'm going to put those on the left, and then you'll see it says to create a directory to hold all the projects called Spring Cloud Gateway. So I'm going to start by doing that. Click there, Spring Cloud Gateway, and then CD into that. And you'll notice it says to create a Eureka server. So I have an alias for that, Eureka service, and you can see it matches what's on the left. So it uses Java 11, uses the artifact ID of discovery service, and it uses Cloud Eureka server as a dependency. So I'll start by creating that. And you'll notice that also it's start.spring.io, so you can use start.spring.io through a browser or through the command line like this. And the HTTP command I'm using is from HTTPi, and you can get that from HTTPi.org. So open this project in IntelliJ, and we'll open the Eureka server class, and we'll go ahead and enable Eureka server. And then we'll modify the application.properties to have the server port of 8761, and Eureka basically turn off registration so it doesn't try to register with itself, because that would be silly. And I am using Java 11, so if I were to go here and type Java version, you can see that it is Java 11. If you don't have Java 11 installed, you can use SDKman for that. So SDKman is a slick tool that can install all kinds of SDKs for you on the JVM, and you'll see this is the command you need to install it. And so if I wanted to see all the different Java versions available, I could do SDK list Java, and you'll see there's everything, even Java 14 here, Early Access, and Java 13, which I do have installed, but I won't be using. And so now we can start that Eureka server with MVN, or CDN to the directory, Discovery Service, and MVN Spring Boot Run. And if you're typing Spring Boot Run all day, there's actually a way so you don't have to do that. You can go into the palm.xml, and down in the build section, you can set a default goal. So you could do Spring Boot Run, and then all you would have to type is MVN. That certainly makes things handy. Once that's up and running, you can go to localhost 8761 and see that no instances are available, and that's because we don't have any other applications that are talking to this service registry. So the next step is to create an API gateway with Spring Cloud Gateway. So I will open a new terminal for that, and make sure and do it in the root directory, and I have an alias for that as well called Cloud Gateway. So you can see it there. We're again using Java 11. We're calling the artifact API Gateway. The base there is going to be API Gateway. We're going to use Actuator, Cloud Eureka, Cloud Fane, Cloud Gateway, Cloud Histrix, Web Flux, and Lombok. So if I run that, it'll create the project for me with all the files from start.spring.io. So I can open that up in IntelliJ. But the first thing I want to create the reactive microservices first. So I'll do that with a car service, alias car service. You can see that that uses Actuator, Cloud Eureka, Web Flux, DataMongoDB, Reactive, Flapdoodle Mongo for testing, and then Lombok. So you need a reactive JDBC driver or database when you're going to do a reactive architecture and so that's why I'm using MongoDB in this case. So if I run car service, it'll create that for me and then I can open that up in IntelliJ and start working on it. So the first thing I want to do in the car service is modify the application dot properties as well. And so you'll need to set a spring application name for all of your services so they can register with Eureka and then you can use that logical name to connect between services. You don't have to worry about ports or IPs or anything like that. So spring application name is car service and then the server port doesn't matter as long as it's not 8080 because we're going to use that for our API gateway. So this could be 9000, could be 8081, I've tested with both, doesn't matter at all. I'm just going to use 8081 for this example to match the blog post. And then we'll add Eureka registration here. So on our main car service application class, we'll do enable Eureka client and then we'll create a REST API that's simply a car service that brings back a list of cars and allows the API gateway to do some filtering and just some simple stuff with that. So we're going to start by creating an entity. This is my live template that I'm using here. So it's called webflux entity and if I hit J, I'm going to spell webflux, right? So webflux entity and then class car. And so if you wanted to see what the live templates look like, if you go to preferences, live templates, mine are under matte-rable shortcuts after you import them. And if you went down to webflux entity, what you would see is this data right here. So you could create your own and the name is a variable. So that's why I typed that in afterwards. And they're very handy, especially for demos. And so I'm going to change this to a name. And then I'm also going to set a release date. So private local date, release date. And I'm going to change this to a UUID because we're using Mongo. And then I'm also going to create a repository to persist that data. So that's under my webflux repo live template, car repository. And then we'll need to change this to UUID as well. UUID, okay. And then we'll need some sample data. So I'm really excited about electric cars. I think even if hydrogen cars are better, they aren't quite there yet. They're a little more expensive. Electric cars are cool. I'm also super into Volkswagen. I have an old classic 66 Volkswagen bus with 21 windows. And I also have a synchro. But I'm excited about the Volkswagen electric cars coming out. So let's use those for our data. What you'll see here is a number of cars that are coming out from Volkswagen. You can go to this website and see the release dates here. There's an ID. There's an ID cross, an ID vision, and an ID buzz. ID buzz is the Volkswagen bus that I'm really excited about. So let's look at what that looks like. There it is. So pretty cool. Images might be better. But yeah, looks hot. I'm excited. All right, so you'll notice that there is this log variable down at the bottom that's not getting resolved. Well, you can do that with some handy annotations from Lombok, SLF4J, for example. And now it resolves that. You just put it on your class. And we can tighten this up a bit. So if we wanted to make it so it's all in one line, we can do that. And you'll notice what happens here is it takes all these, puts them in a list or a set, and then it deletes everything so the database is cleaned. And then it goes through those VW concept cars, flat maps them and saves them all into repository, and then finds them all. And if you don't subscribe, none of that will happen. That's one of the natures of reactive programming is you have to have a step that actually makes it all happen. So that's what we're doing with the subscribe right here. And at this point, we could actually run the project. But one thing I want to point out is if you're doing this from the command line or you're doing it in an IDE that doesn't have Lombok installed, you'll probably get errors at this point. So I do have the Lombok plugin installed. And if I went to plugins down here and search for Lombok, you'll see I do have installed. So if you experience any compilation errors while you're doing this, that's what you need to do, you can install that and you'll be good to go. And the other thing is MongoDB. So you can install Mongo using like Homebrew or even as a Docker container. The blog post has all that information in it. In fact, we can scroll down and see it's one of the first steps here. Come on, baby. There's the ID buzz, I like that. And so you can install it here with Homebrew or you can use Docker. You can also just comment out the flat doodle dependency. You gotta love that name, right? So in palm.xml, you can just say instead of test scope, just set it to compile scope. And what that'll do is it'll start it up and it'll be available so you can use it. So that's pretty handy. And then rather than just start it up and show you, I'd rather do is write a test. So there is a test created for you by default, this car service application test. There's nothing really in here. So I'm gonna make it a little more robust. And what I'm gonna do is I'm gonna use that car repository and post a URI to cars endpoint and then exchange it and expects that the status is created and then verify that data came back, right? That matches what I put in. Get all cars the same way and then deleting cars too. And I don't even think I need this car repository in here. So I'm not using it. I'll throw it down at the bottom, I am. So nevermind, leave that in there. And then we run this and who thinks it's gonna work? Probably not, but it's important for it to fail first. So if you actually wrote a test, it succeeds. So the reason that it does fail is 404 not found. So if we look back at our car service application, there is no rest endpoint, right? The one way to do it is you can do resource or rest resource. There's some advice you can put on there or annotation and then it creates a rest endpoint for you using spring data. But I'm gonna just create a controller that does everything. So web flux controller. And you'll see here it's a car controller uses that car repository, has a post mapping for cars so you can create a new car. Also has a get so you can get all the cars and then also has a delete. So those are the three it has. It doesn't have any puts just for simplicity. And now if we were to run our test, it should pass. So now we have everything working in our reactive microservice. Now let's do a little bit of work on spring cloud gateway. So first thing I'm gonna do is create an aggregator palm that I can use to open up all the projects at once. So I'll just call this ag palm.xml. I have a shortcut for it to create it. And then we'll go ahead and shut down this project. And this one, and we'll leave IntelliJ there. We'll exit out of here and we'll cancel that one. And then go back here. And we can move that car service, ag palm that we created to just palm.xml and we can open that one up in IntelliJ. Now what this will do is it'll prompt us. Because it recognizes that there's multiple spring boot run configurations, you can actually show them in IntelliJ and run all the apps at the same time. So that's a nice handy feature of IntelliJ. If you were to expand this, you can see all the various ones that you can run. So in the API gateway application, we need to enable Eureka Client and then spring application name. And then in the client, we can create a route locator bean or in the API gateway application. You'll notice this is just a custom route locator and we're using the builder to specify the routes to car service, matches that cars and uses load balancing. That's what the LD stands for right here. And so we could start all three apps. I like to start the Eureka server first because the other apps need to register with that service. Otherwise they can't talk to each other. And you'll notice up here, it's using Java 13 here. That's because I do have it configured in my IntelliJ, so it ends up pulling that one first. But hey, spring 218 runs just fine on JDK 13. And we'll start up our API gateway and we'll start up our car service. And then if we were to use HTTP, we can do 8080 since local host is the default and hit cars. And this does happen from time to time when things aren't actually up and running. So when this happens, what I like to do is I like to check 87.61. Looks like they both are up and running now, so let's try again. And still it's coming back with that. So let's try in the browser, give it a little time to start up. One more time, boom. So I have seen that. I think in production, if you start them in the right order, then it should be okay. I did start the API gateway before the car service, so maybe that's the problem. But I like to keep these kind of issues in there so you know it's not you. It's just one of those things that happens. So now I'm gonna add a load balanced web client because I'm gonna use the web client to talk to the server. In this example, so web client builder is a shortcut for that. You can see it's a bean, uses a load balanced annotation and it returns a web client builder. And then I'm gonna create a car pojo. So this is just gonna be a class called car and it's got a private string name and a private local date release date. And then we're gonna use the Lombok annotation so we don't need to write getters and setters or constructors or two string or hash code or all that. And then I'm gonna add a web flux controller fave. And so what this endpoint's gonna do is just gonna return my favorite car, which is the ID buzz. So you can see it uses that web client builder. It goes ahead and talks to that car service cars and notice it's not using any IP address or DNS name or anything like that. It's just using what's registered with Eureka. It retrieves and does a body to flux and then filters out and makes sure it only returns a favorite. So if the name equals ID buzz, then that's the one I get. So now we can save that and restart the API gateway. Now if we go to fave cars, you'll notice that's the one we get. So great, that's working. Now let's add some failover with histricks. So histricks is a way of basically failing over and having default data available for the client. So it's a very nice thing to do. You don't wanna return a 500, that's not very nice. So we had a histricks filter here and you'll see it sets the name of cars fallback and then sets a fallback URI. And since we've created that, we have to actually create a class to handle that. So I'll create a histricks controller. You can see it's a REST controller. It's called cars fallback. That doesn't really matter. The endpoint is what matters. That needs to match what we have right up here and it does. So it's just gonna return an empty flux. So we'll save that and restart the API gateway. And now if we were to go to cars, that all works. And if we shut down the car service application, then instead of getting a 500 or it can't reach it like you saw before, you will just get an empty array, which much nicer. And then if you were to start it up again, then it would restore itself once it started up and you'll get that data again. Like so. Pretty slick, huh? So the last thing I wanted to show you or a couple of last things is how to secure this, right? It's great having a microservices architecture, but chances are you maybe need to know who the user is, who's logging in, and give them back some data that pertains to them. So there's this handy thing that we created called the octa maven plugin. So if I were to go into the gateway, which I am in, I can run mbm.com.octa, octa maven plugin setup. And what this will do is it'll prompt me for a few things. My name, my email address. You have to get this right or you won't get the activation email and you won't get your temporary password and you won't be able to log in. So make sure you type that right. I work at octa and it's creating a new octa organization for me and this may take a minute. So there you go, it took about 30 seconds and it created a new octa org and an OIDC application that works with Spring Boot. Not only that, but it put all that information in my application.properties file. It did not modify my pom.xml to add the octa Spring Boot starter, so you will need to do that. My shortcut is octa maven boot and the latest version is one, two, one. And then while we're in here, I'm gonna add a dependency for Spring Cloud Security because we will need that as well eventually and that is going to be in group ID org Spring Framework.cloud. Okay, and then enable auto import so those are taken into account and then it sent me an email with my activation information. So it says activate your octa developer account and you can see here there's a temporary password so I can just use that to log in. So I can restart the API gateway and I can hit 8080 and there is an issue here so that's strange. When this happens, it says the redirect URI parameter must be an absolute URI. So that's actually in the browser so I think we could do a better error message here instead of saying the redirect URI parameter must be an absolute URI like actually show this URI and error message but you can see here the problem is because we expect you to use localhost. So if I use localhost, it'll prompt me to log in. I have Matt Rabel, you don't need your full email. Oh, I didn't copy and paste that right. So go back to my email. So grab that, paste it in there and it'll ask me to enter a new password so I'll use one password to do that, generate a password, save and fill and then what was your dream job as a child? My favorite movie quote, first computer game you played like all kinds of them, right? Favorite sports player, Larry Bird and then we'll use San Francisco and create my account and it'll actually create your account and then redirect back to the app. The reason for this error is because well, first of all it never came to the proper area so let's try just localhost 8080. So now it says 404 and that's because there is nothing in the homepage so if we go to cars now we're logged in and everything works as expected. So that's all working, right? We have OAuth2 and OIDC installed but our API gateway simply acts as a way of redirecting and logging us in. If you add a spa for instance, a single page application that needed to talk to this gateway, you might wanna set up cores, cross origin resource sharing that allows any client on a different port to talk to it. So let's go ahead and do that and set it up as a resource server so it can actually process access tokens without having to hit the URL and log in and all that. So I'm gonna create a security configuration class and so this class is enabling webflux security, enables reactive method security. We aren't gonna use it but it's certainly a annotation and then this is from an old demo that I had so remove that one. CSRF, if you wanna have CSRF with your React or your Angular client and you want that Angular client to be able to read the CSRF cookie and send it back as a header, then you need to say with HTTP only false, you can also just CSRF disable like so. Disable like that, so that's an option too. And then it says any exchange has to be authenticated. It supports OAuth2 login, so if you hit 8080 it'll redirect you and it also supports OAuth2 resource server which will process a JOT if you send it as a bearer token in an authorization header. And then down here is the course configuration and so to test this, I'm going to create a test, we'll call it it's webflux course test is my shortcut. We'll make sure that cross origin resource sharing is actually working and then import some Moquito static imports. And so this is a test that basically uses web test client and a reactive JWT decoder that's mocked and it says create a JWT and return when it's decoded that JWT. And so it goes and heads and sends an origin of example.com and expects it to come back with access control allow origin all. So that's what we have configured in our security configuration right here. We'll change this to star and then we can run that using IntelliJ and you'll notice that does fail. I have seen the course configuration fail. So what I've done instead as a workaround is there's a webflux course filter you can use. So it uses a course web filter sets it as a bean and it uses a lot of the same configuration and classes. And if we run that one, then it should work. And now it does. So we know that we've created a resource server. We also have a lot to log in configured with spring security and the whole API gateway is secure. But there's no secure communication between the API gateway and the reactive microservice that car service. So let's go ahead and make that work. The first thing I'm gonna do is just copy this down into the car service. And you could use something like spring cloud config. I have another YouTube video on using that to configure all of your microservices. So that's a better way than putting it in each individual application up properties. You could also use environment variables and configure it that way. So those are just a few ways you can do it. So we'll go to application up properties for the car service. We'll add those properties there. We also need to add the Maven plugin for our Octoboot starter. One, two, one is the latest version. And we don't need spring cloud security in this one, but we will need a security configuration. So I'm using enable web flex security one more time. And I'm also using that security web filter chain, but I'm only specifying an OAuth2 resource server. I'm not specifying that OAuth2 login. If I want that OAuth2 login to work with this, then you would have to go add a redirect URI for 8081. And for the most part, this shouldn't be exposed on the web, but we're just locking it down so we don't have to worry about it. And then this configure resource server 401 will actually just return a 401 even in the browser. If you don't have this in there and you just use spring securities, a lot of support, for instance, you'll just get a blank page in your browser instead of at least something explaining what went on. And so we can restart our car service now and show that it'll be protected and return a 401. So 8081 cars, and you'll see it returns a 401 unauthorized. So we need to configure the API gateway now to pass an access token down to the reactive microservice or it won't be able to authenticate. So I'm going to modify, first of all, the test for this class because if we try to run it, now that we've added security, it will not work. So let's try to run it just to verify that that doesn't work. You see our test did fail with 403 forbidden. So I have a shortcut for this. And again, the Makito imports. And then if we were to look at the local history, you can see what I changed. So you'll see we added those imports and the biggest thing is we're mocking that reactive JWT decoder again. We're setting up that JWT and we're adding it to the header. And so that basically allows us to mock out the authentication and everything will just work in our test environment. And we won't be hitting octo so you don't have to worry about any rate limiting or anything like that. So that's a great way to do things. Now our test pass. You gotta love that, right? Love the green. The green is great. Now back to our API gateway application. We need to modify this to actually add a filter that applies a token relay gateway filter factory to this route. So I'm just gonna replace the whole thing. Router locate token. And you'll see we pass in a token relay gateway filter factory. And this class is from that spring cloud security dependency. And we're just filtering and applying. So a one liner to actually take and put that access token in an authorization header as a bearer token and send it down to that car service. Pretty slick I think. So now after we restart our API gateway we should be able to hit that same cars endpoint and be logged in. And you'll see there it is. And to prove it all works I can do a new incognito window. But you know if I did that I never even saved my password so I forgot what it was. I don't even know what org I was in. But hey it works and that's why it's retained. But if I went to a new incognito window and tried to go to local host 8080 cars you'll see that now it prompts me to log in but I threw away that password. I don't know what it is. So I don't even know how to recover it. I guess with those security questions that's the beauty of those. You can go here. You can say forgot password. And then reset it that way. So that is the gist of everything. You can see there's a GitHub repo. If you were to go to that repo and go to Spring Cloud Gateway. This is where everything's at. As far as if you wanted to run it you could just clone the repo and actually run this project on the main. Read me there is instructions. So if you go here you can see that you can clone it, you can see the into it. And then you need to actually set it up for Okta which you can do with that Maven plugin. And then you can also do it manually if you already have an account. And boom, you're good to go. You're developing a microservices reactive architecture and it all works with Spring Cloud Gateway. So that's pretty slick. If you liked this tutorial I would encourage you to follow my whole team on OktaDev or on Twitter. We even have a webinar coming up here see on October 17th. My good friend Micah Silverman will be doing a talk on OAuth 2 and OpenID Connect. You can follow me on Twitter at mrable. Never build off yourself. And then if you like this video of course subscribe to our YouTube channel. There's a big subscribe button right here and we have a bunch of other videos that you might like. So have a nice day. Peace.