Coding Shorts: Structuring Minimal APIs - A First Draft

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back to coding shorts my name is sean wildermuth if this is your first time here or you've been watching and aren't subscribed please subscribe it really helps me in today's episode i'm going to be talking about minimal apis and net 6 again this time i'm going to be digging into some ways to scale it out i've talked about minimal apis for very small discrete apis before and using mvc controllers for large projects but there is a middle ground and i don't know if this is the right way or if it isn't but this is sort of my first draft of some things i've been thinking about please in the comments tell me how i'm wrong how i'm right how you would improve it we can all learn together let's get started [Music] so i'm now using the latest version of visual studio 2022 as well as the rc2 of that net six and that's really what we're going to talk about hopefully the apis won't change so much once we get to release in a month or two but i wanted to talk about some of the consternation out there about minimal apis so if we start a new project here and i'm just going to use an asp.net core api the standard api project in asp.net in this case i'll just call this first we're not going to finish creating this but i'm just going to create this new project and when the options come here one thing you'll notice is there's an option for use controllers and this will remember your last step but this means we can go back to using non-minimal apis but to actually use controllers and what happens here is that we're continuing to use all the same things we're used to in the startup class they're still using the top level statements but they're just using controllers and endpoints etc to make this work in the way we want to and what's interesting about this is this is very similar to what you would do in the startup class before then there isn't really an option to create that startup class but we could certainly build this out the way we want but that's not really what i'm going to dig into i want to talk about using minimal apis not just top level statements but i do want to show you the top-level statements don't imply that you have to be using minimal apis you can use whatever middleware and service layers you want in this case they're using the magic map controllers as well as adding controllers to the service layer to make that work just like we've always done with net core so i'm going to get out of this this is going to leave us in visual studio code where i have a similar project set up and in this project to sort of clean this up i have a program.cs and this program.cs is using minimal apis let's see what this looks like i have an entity framework context object i'm registering as well as a repository pattern for getting at that data so pretty typical and i'm adding these to the container directly and then down here i'm configuring all the middleware and some of this middleware are these app gets and app posts and as put and in this case i'm proposing an api for a set of clients and those clients have get the entire collection and get an individual object in the collection create a new collection with post create a new item with post update that item with post update that item with put and finally delete an item right typical crud sort of work that we might want to talk about and this is only like 90 lines of code this is a pretty simple api creating it with minimal api and writing something this long might not be an issue but as you start to create larger projects you don't want program.cs just ballooning because of this possibility of ballooning this is one of the reasons why some people are sort of adverse to this new idea about it but i want to talk about what i think is really going on here and that is top level functions are really allowing you to organize your code in any way you want instead of having this sort of more rigid idea with the startup class i think the startup class being a generic argument was meant to give you more flexibility but it didn't really do that everyone just kept it there and so i'm going to make an approach to structuring this a little better and talk about those sort of pros and cons and even the pros and cons according to controllers and i want you to see what i'm doing here this isn't the right way this isn't the gospel this isn't the best practice because it's so new i don't know that we have best practices yet but we're gonna take a stab at it and then we're gonna have a conversation after this video is out to tell me how i'm wrong to let's as a community figure out how to make it better so i'm gonna start with the low hanging fruit and that is i hate kind of the way we're building the services here because as this grows it's just going to become a sort of a muddled mess and so one of the first things i want to do is replace this with just register services i'm just going to create a method for that and if we come down here i'm just going to create a new void register services and i'm going to put the same code in there now if you haven't done a lot with top level statements this might be a little confusing because the builder isn't passed in here the nature of the way top level statements work is because remember it's behind the scenes compiling a main and a startup class for you that one of the philosophies around it was instead of having to pass everything into here like you would if they were static methods everything in this scope up here will also be in scope so we can continue to use that builder object here and call them as if they were in the public i don't love that because it's a little bit more verbose and so i am going to make the decision to pass in an iservices collection or i have service collection i believe it is and then i'm going to use that just to make these a little tighter and more readable because the fact that we have a builder in scope is fine but there's not a great reason for it and so if we come up here i'm just going to pass in builder dot services just to make that all work now all we've done is structure this which is really two lines of code into three lines of code or really with this line of fourth line of code but i feel like i'm doing a better job of future proofing it because this is not going to just have two dependencies add in for an item we're going to get more as we go on and so i'm also going to rename this because i hate the full name builder i'm just going to make a little bit more concise clearly that says builder and i'm fine with that and now i want to configure the middleware so having things like http redirection here makes total sense but we'd like to be able to wrap up all of these maps in a little better way see what's complaining about here oh it's give me a suggestion to make it static i'm not going to make it static could make it static there's nothing wrong with making it static but it does just adds a tiny bit of overhead and i want readability versus purity of function if you've been watching these videos you know readability to me is king because of all the maintenance this code will eventually take it's not about eking out every cpu cycle or every i o transfer it's often about readability at the expense of them up to a point a lot of our applications don't need to run so fast that we need dedicated hardware or things like that for it it's the functionality and the business rules that matter so how do we want to do this i think for me i will say create a class called client api and i'll create it and i'll just call register could make this static but this is just good enough and we'll see why we might want to actually create it and we don't have this class so i'm going to generate the class in a new file and there we have our nice internal though i'll make it public because not a big fan of the whole internal thing and this class is going to have a public constructor that we're going to want because we're going to register it with dependency injection so that we can use function and constructor injection here come down here and i'll just say public void register web application app and then we can start to actually use that app to register things right so let's come back here and let's take all of our objects here because i'm going to do all that registration for the api directly so i could just paste this all in here right this will all work and in fact we bring in those namespaces which probably should be global namespaces but that'll do for now uh let's see if we need anything else we need the client imported as well oops make sure we bring those entities in let's reformat that and with just that simple humble change you can see that by registering these and we're using injection in the actual method and the put and such that this should still continue to work so let's run it and see if i've screwed anything up i have a little console here let's look at get if i send to get all the clients oh server name wrong i think it's 4 8. i had to add the s for https we can see that it's now working even though all we've done is sort of kicked the can down the road so we'll close that for a minute we'll talk about what we really want to do i like the way this works in that we are using some registration here but i want to do a couple of other things that i think are important to this api first of all i would like to break these out so that they're not part of the registration and so let me show you that with that first get what i really want is public and i'll say get all because this is one where it gets all of the objects and of course this is going to be a task because this is an asynchronous operation and we're going to return a results object this is the part that's sort of inferred by what ok returns here i'm sorry i think it's i result we say results and we'll find out why it distills it like this that might be out of its scope yeah that's better so we've got that simple get all of course it's not a lot of code so this isn't going to matter as much for the simple one and then what are we going to do here we're just going to say get all right i'm going to specify that this method that we might want to be able to attach to and do other things is going to be get all and so i will quickly do this for all of them so you can see what this looks like afterwards and we'll go ahead and speed it up so you can see all the magic happening really really fast actually i haven't started yet now with that done we can see that the registration becomes fairly simple and then each of these are just the name of references in our class right and these could probably be private but for now in order to pass us to it i think we might need public so i'm not going to worry about that part because again we're not trying to figure out the perfect way to do this i'm trying to talk about different ways and at least my first shot at sort of normalizing this and so back here in the program this should all continue to work right we haven't really changed anything we've changed how we're structuring this but it still should continue to work so let's come back here let's restart it to make sure that it's actually using the latest version of the code still working we're not sure we're running the latest version of that code so we can see this is still working that's great but i don't feel like we're really doing this any justice because we have these dependencies that we're shoving into each and every one of these right and in fact we have some things i'd like to be doing like in the catches i'd like to be doing something like logger right this is a pretty typical thing log error let's go ahead and log this whole thing just as an example right so up here i might want to use injection of saying i logger client api logger create a field for that and i'm just going to rename this field because i like them to be underscored so now we can use that logger in case we're having an issue but of course this doesn't work anymore because our code here can't create this so what are we going to do instead we're going to register our service add transient client api and then we'll save our the api will be what the api is going to be app.services.get service deploy dpi and here we'll say api question mark dot register in fact i don't want to just not run it in case that happens i'd love to actually have something happen so i'll say if api is not null then make this happen otherwise we'll just end because something really bad has happened right and i think we can go with that because i've captured whether it's not null here and is running and registering and let's stop this and start it over still working great and so we've got that great sort of solution of we've broken it down but there's something that's still sort of nagging at me couldn't i just have them inject the repository as well right well i'm not going to change them here yet but i am going to see what happens if i try to do a i juris repository repo and let's go ahead and create a field for that and if some of you have figured out why this isn't going to work i applaud you it took me a minute to sort of get my head around what was actually happening so this time something interesting happened it said unhandled exception actually through this error says from root provider because it requires a scoped service what does that mean in case you don't know about scope services the idea of a scope service is one that's going to be created whenever there's a bunch of work that needs to be done and for every request asp.net creates a scope therefore when we want to inject this or this they're both scoped services we need to be inside of one of those scopes this is transient this can be created at any point and because we're trying to inject these it could mean that we might need to create a scope around this but that's not really the problem the problem is this it's that in the client apis the way that we just get rid of this the way that minimal apis works is just different and that is it has a pointer to this method and it's going to be doing injection into the method itself into the parameters in order to successfully call this so instead of having a more complicated longer lived object because in asp.net mvc when it uses controllers it defines a route maps that route to a controller into a controller's method and part of that is constructing for us the controller which it does through the service layer and then the controller has all that state and then it can call one or more of the methods on it that means constructing and destructing an entire object every time doing injection at the class level this class is only ever going to be created from the service layer during registration that's it then after registration it has a reference to each of these methods and it's going to attempt to fill the dependencies to the methods the class doesn't really need to exist anymore other than a placeholder for holding on to the references to the methods and so therefore we're going to want our injection here as well as our parameters are being passed in based on whatever the route map or the route matching is here so we happen to have id on a few of these because we have the whole object and even on the case of post we're sending in the whole client just like we've done before in asp.net and vc as well so we're getting a lot of that same behavior it's a little lighter it's a little less tied to each other and this is just a convenience way so that we know as we're creating larger and larger projects how we can create them in fact one of the ideas i had was to extract an interface and we'll actually put this in another place but this interface i actually call something like api because the only thing that is in fact on this interface is what register so what this allows us to do let me put this in another file because i'm not sure why your studio code's a little different than that but there we got it api is we could ultimately change this code to say app.services get services of what i api for each api in apis and we'll complain about that in a minute and i'll explain why and here we can say for every api if it's not null registered if we for some reason got none it'll drop down to app run and we'll need to continue changing it or i can just say if is null throw program exception about api is not found right and then just register each one of them and then what happens oh i guess i need to say neo i hate that part and then what happens is all i need to do is register all my different apis with that interface and then it should be able to find them all and so this should still work right again this is just an idea this isn't a requirement but if i go ahead and run that with that change let's see if it also works back to our get let's get rid of that giant thing let it think continues to work and so the structure i've been applying here isn't the best isn't the way we're still learning because this all this is pretty new but i'd love you to reply in the comments about whether you think it's the right approach a wrong approach i don't want to over engineer this thing but i also want to provide some structure and there's some middle ground that i don't feel like i've quite met but i feel like i'm getting somewhere close to now it may be that this whole idea of how we do this is close enough to controllers that you might just go ahead and go to controllers and i don't think that's a bad idea because to me there's also a path of maturation i start with minimal apis here directly in program.cs to get my project off the ground in greenfield and then as i move forward i might go i'm adding some complexity oh no this is too big okay i might move myself to this api system or i might go right directly to controllers because once you know i have 15 controllers or five controllers or whatever the magic number is there's so much complexity in the simplicity of this that just using the infrastructure of model view controller or controller based apis is going to be the right solution so those are my thoughts if you've gotten this far in the video thank you so much don't forget to like and subscribe that really does help me you can see my other courses especially ones on apis at pluralsight and thanks for watching another coding short [Music] [Music] you
Info
Channel: swildermuth
Views: 839
Rating: undefined out of 5
Keywords: .NET, .NET 6, Minimal APIs, C#, REST, API, Web Development
Id: Q_zXFeP-QNI
Channel Id: undefined
Length: 20min 36sec (1236 seconds)
Published: Sat Oct 23 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.