7 Simple Steps to Write a Better API Using Spring Boot that You Should Know

Why no one taught me that?

Why no one taught me that? Photo by Ben White on Unsplash

Hi there! I hope that you are having a great day, today we will talk about some advice that I consider essentials when programming a Spring Boot application. All of them are based on my experience working with that and are subjective of course, let’s go for it.

1. Organize the project properly

This sounds a little obvious but not less important, maintaining a great structure is a big thing to have in mind. Keep things organized is very helpful for us but it is super helpful when new people enter the project, the learning process will be much easier if things are where they should be.

2. Use @RestController

If you are starting with Spring Boot I’m sure that you know the @Controller annotation from where you were studying it in a course, it is totally fine but I recommend you to use the @RestController alternative. This annotation will wrap all the responses that the controller returns into ResponseEntity objects.

Endpoint example in a @RestController class (Made with https://carbon.now.sh)

The endpoint definition looks very clean this way, you can specify the HTTP code returned with another annotation and a lot of more details if you want.

3. Use Optional as a return parameter if necessary on the repositories, entities and models.

This is very helpful to avoid NullPointerExceptions and much more easy to see when a property or a method call can be null. If you are using Spring Boot 2+ you probably saw that the default implementations of JpaRepository returns Optionals, you also can do it with your custom queries just like that:

Using a repository with optional return value (Made with https://carbon.now.sh)

This is a huge improvement, we can get the result and throw an exception or use a default value very easily using the Optional tools.

Also, another thing that I recommend very much is to return Optionals on the getters of your models or your entities if the attributes can be null, just to be sure that the person that codes with that later is treating the value as it should be. With collections I prefer to return an empty list, so the person that uses it doesn’t have to unwrap the optional, just use the list normally. This simple approach can save us a lot of time in the future, I’m used to doing it this way:

Example of getters with optional values (Made with https://carbon.now.sh)

You can read a little more about optionals in my last article here if you want :)

4. Use dependency injection in constructor, avoid dependency injection on fields

You can see @Autowired annotation everywhere but using this is not a good practice. Here you have a couple of reasons why:

  • Hidden dependencies: if someone wants to create an object from a class that uses field-based dependency injection they couldn’t do it properly because the @Autowired annotated variable is not required by the constructor and it will be null. Using dependency injection through the constructor we can clearly see what is needed to create an object.
  • Can’t define autowired fields as final because the dependency injector container has to modify it after the instance is created.

5. Keep models and entities separate

Never return an entity outside the API. Sometimes you have to return the exact data that is declared in the entity, you are tired and decide to return the entity… Two months later you have to change the database definition and now this affects your entire application, all 3 layers (Reposity, Service and Controller). Maintain separate the models that we return outside the API and entities of the database allow us to have more control in that kind of situation, so even if the data that you have to return is the same as the entity’s (for the moment), create a model, just in case.

6. Validate the attributes of the models and entities with annotations

Don’t mess up your beautiful code with validations please, you can easily annotate the attributes of your models to accept only data that fulfill all your requirements. I’ll show you an example:

Example of a model with validations (Made with https://carbon.now.sh)

You can use the same approach to validate your entities and your application will be free of validations in the business layer. You can even create your own annotations for custom validations, I always use one to avoid null values inside of lists, just like this:

Example using the @NotNullValues annotation (Made with https://carbon.now.sh)

7. Use @ControllerAdvice to control exceptions

Managing exceptions with Spring Boot is a very easy task, just throw the exceptions to the controller level and the @ControllerAdvice will do the rest.

Controller advice example (Made with https://carbon.now.sh)

Using the @ControllerAdvice you only have to catch the exceptions that you want and specify the response that you want to return in every case. This can be complemented with a good exception hierarchy and you will have a great exception management.

I hope that you enjoyed reading! If you want to read further about the terms that I used here I’ll give you some links to great tutorials:


Helping brands provide irresistible search. Pairing software with interfaces to combine function and beauty in one. From mere results to meaningful relations and engaging interactions.

Cristian González Morante

Written by

Backend engineer at empathy.co — I’m passionate about technology and software development. Love spending time learning and sharing knowledge with everyone ^^


Helping brands provide irresistible search. Pairing software with interfaces to combine function and beauty in one. From mere results to meaningful relations and engaging interactions.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade