A simple .NET Core API design

cesarcodes profile image Cesar Codes Updated on ・3 min read

Sometimes we don't need to over-engineer applications by introducing a ton of layers. I recently had the chance to rewrite a small MVC application as a .NET Core REST API and followed the following approach:
The application pulls data from an existing SQL Server database and from existing Stored Procedures. So, my repo looks like this:

As you can see, I'm overriding the OnModelCreating method where I manually map the properties of my Item model to column names in SQL Server.
Ok cool, so then on top of that I have a repository whose only job it is to retrieve 1 or more of these entities:

I then have a service layer which receives the entities. Ideally, you could use AutoMapper here to do object mapping, but it just so happened my project did not require it and I don't like to over-engineer. But anyway, the job of the service layer is to retrieve the entities and prepare the objects to be sent out by the API. It looks something like this:

As you can see, the service gets the entities from the repository, performs additional validation and returns an ApiResult. Shoutout to my friend Eric for teaching me this pattern. Anyway, as you can see, if the repo returns no items, the service returns an ApiResult with ResultCode.Empty and when there are items found, it returns an ApiResult with the items list.
If you look at the ApiResult class below, you'll notice that it has 2 constructors. The first constructor takes in an ApiResultCode, so in the case above where there were no items returned by the repository, we send a ResultCode.Empty to let consumers of our API know that there were no items to be retrieved. The second constructor takes in a generic type and automatically sets the ResultCode to ResultCode.Success. In the service example above, when we got items from the repository, we returned them via this second constructor.

In case you're wondering what the API response from this API Result looks like, it would look something like this (except that the data property would be a serialized JSON of your service payload):

This could help you with various things, such as logging, but could also help consumers of your API program around it. For example, if you're building a JavaScript front-end for this API, you could do some logic based on the resultCode - you know if the response is empty, if resultCode is Empty, for example.
Even neater, you could do operations on this ApiResult on the C# side of things as well. For example, in the following example, I have created a base controller class has a generic ApiResponse() method that I can use in my other controller methods. Because, why would I repeat the same logic over and over and over?

Lastly, I encapsulate all of the endpoint parameter validation logic through the use of IValidatableObject, which I've covered on my other post.

Happy Coding!

Posted on by:

cesarcodes profile

Cesar Codes


I experiment with code and talk about it.


markdown guide

Hey, thanks for the post.
I'm really interested in .NET (I think is is great framework (not only) for web development. But I'm really stuck in the situation, that I am only *BSD / Linux user. Do you have any tips for development on these platforms (I tried to do some stuff on my old Windows laptop with Visual Studio installed, but it was really unpleasant experience)? Thanks for any tip!


Yeah, on non-windows platforms you're well off downloading Visual Studio Code and getting familiar with the .NET CLI. Just learn the basic commands like dotnet new, dotnet restore, dotnet build, dotnet publish, dotnet run. to start. Then, you can learn how to use the C# debugging in VS Code. One more thing, I am currently working on a high quality content YouTube channel in this very topic, if you're on YouTube, it would rock if you could subscribe to my channel so you can check out the content as I release it (very soon). Good luck bro let me know if you have any other questions.


Thanks a lot, this was very helpful! Also I'll subscribe.


Hello! I find this very interesting ... but why replace the HTTP status codes with the ApiResponse codes? Are they not descriptive enough by themselves?


Good question! It’s an optional thing. In projects I’ve used it on it did allow us to do some additional things related to business processing. In other words, they were mainly for my own use cases, you could just take that part out of it works for you.