Building APIs That Don't Suck: A Spring Boot Journey
Lessons learned from building REST APIs that people actually want to use (and maintaining them at 3 AM)
I’ve built a lot of APIs. Some were good. Most were “good enough.” A few were trainwrecks that still haunt me. Here’s what I’ve learned about building REST APIs with Spring Boot that won’t make your teammates (or your future self) want to cry.
Why Spring Boot Though?
Let’s be honest - Java gets a lot of hate. “Too verbose.” “Boilerplate hell.” “Why is everything an AbstractFactoryBean?”
Spring Boot fixed most of that. It’s opinionated enough to get you started fast, but flexible enough to not box you in. Plus:
- Auto-configuration that actually works
- Embedded Tomcat (no more XML deployment nightmares)
- Production-ready features out of the box
- A massive ecosystem for literally everything
- Great documentation (rare for Java)
Is it perfect? No. But it’s solid, battle-tested, and won’t randomly explode in production (unlike some frameworks I won’t name).
The Anatomy of a Not-Terrible API
Let’s build something real - a basic user management API that doesn’t make API consumers want to quit their job.
Project Setup
<!-- Core Web stuff -->
org.springframework.boot
spring-boot-starter-web
<!-- Database magic -->
org.springframework.boot
spring-boot-starter-data-jpa
<!-- Validation (please validate your inputs) -->
org.springframework.boot
spring-boot-starter-validation
<!-- PostgreSQL because we're not animals -->
org.postgresql
postgresql
The Controller (Where HTTP Happens)
// Version your APIs, past you will thank future you
The Lessons I Learned the Hard Way
1. Use DTOs, Not Entities
Bad:
public User
Why bad? You’re exposing your entire entity structure. Client sends a password hash? Saved. Client sends an admin flag? Now they’re admin. Congrats, you’ve been hacked.
Good:
public record
public record
2. Exception Handling That Doesn’t Suck
Global exception handler with @ControllerAdvice:
3. Pagination Is Not Optional
If your endpoint returns a list, paginate it. Period.
public ResponseEntity
4. Database Queries That Don’t Kill Performance
N+1 queries will destroy you:
// BAD - fires N+1 queries
private List orders; // Fetches orders lazily = N extra queries
// GOOD - fetch with JOIN
Optional ;
Use projections for list endpoints:
Page ;
The Performance Stuff That Actually Matters
1. Caching (Use it)
2. Connection Pooling
# application.properties
3. Indexes (Add them before your DB melts)
Things I Wish Someone Told Me Earlier
- Version your API from day one. Adding
/v1/later is painful. - Log everything important (requests, errors, slow queries). Future you debugging at 3 AM will be grateful.
- Write integration tests for critical endpoints. Unit tests are great, but integration tests catch the real issues.
- Document your API with OpenAPI/Swagger. Consumers will actually use your API if they understand it.
- Rate limit from the start. Waiting until someone DoS’s you is… not ideal.
The Reality Check
Perfect APIs don’t exist. You’ll make tradeoffs. You’ll ship bugs. You’ll realize your architecture needs refactoring 6 months in.
That’s fine. Ship something that works, iterate, and make it better. Just avoid the obvious pitfalls:
- Exposing entities directly
- No pagination
- No validation
- No error handling
- SQL injection vulnerabilities
- No logging
Do those things right, and you’re already ahead of 70% of APIs out there.
Resources
- Spring Boot Docs - Actually read them
- Baeldung - Saved me countless times
- Spring Boot source code - When docs fail, read the code
- Random Stack Overflow answers at 2 AM
- Dhanur