As a new developer, the world of backend programming and its technologies can seem intimidating. I can say that with the utmost confidence because I'm a new developer myself. However, I've discovered several concepts that helped me overcome this intimidation. In this article about Representational State Transfer Application Programming Interfaces (REST APIs), I'll share the core concepts that helped me build my first backend project from scratch, along with valuable learning resources I've found along the way.
Getting Started with the Right Tools
The first order of business when building any API is gaining a deep understanding of your tools. In my case, this meant learning core Java, Spring, and Maven. I began my API journey by exploring Spring framework resources, starting with the official Spring REST services tutorial. While it's a lengthy read, it's an invaluable knowledge source!
From there, I set up a basic Spring project configuration using Spring Initializer. This tool makes it easy to bootstrap a new Spring application with the dependencies you need. I initially tried to Google my way through the first half of my project—an approach I don't recommend.
Revisiting Core REST API Concepts
Midway through my project, I realized I had significant gaps in my understanding. I decided to revisit the core concepts of REST API architecture:
Representational State Transfer was first introduced by Roy Fielding in 2000. Since its widespread acceptance, RESTful services have become fundamental to web-based API development. To clarify, REST is an architectural style, not a protocol like HTTP or a standard like the OpenAPI specification, though both directly correlate with REST APIs.
The Six Principles of REST Architecture
1. Uniform Interface
The uniform interface constraint simplifies and decouples the architecture. It consists of four sub-constraints: resource identification in requests, resource manipulation through representations, self-descriptive messages, and hypermedia as the engine of application state (HATEOAS).
In practice, this means each resource has a unique URI (Uniform Resource Identifier), and you use standard HTTP methods (GET, POST, PUT, DELETE) consistently. For example, to get all books in a Book Management API, you'd use GET /api/books rather than something custom like POST /api/fetchAllBooks.
2. Client-Server Design Pattern
The client-server pattern separates user interface concerns (client) from data storage concerns (server). This separation improves portability across multiple platforms and allows components to evolve independently.
In a Spring Boot application, your controllers act as the server endpoints that respond to client requests:
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public List getAllBooks() {
return bookService.findAll();
}
}
3. Statelessness
In REST, each request from a client must contain all the information needed to understand and process the request. The server doesn't store client context between requests—each request is independent.
This means you shouldn't rely on session data. Instead, any state should be passed in the request itself or maintained by the client:
@PostMapping("/checkout")
public ResponseEntity checkout(@RequestBody OrderRequest orderRequest,
@RequestHeader("Authorization") String token) {
// Token contains authentication state instead of server-side session
User user = authService.validateToken(token);
return orderService.createOrder(user.getId(), orderRequest);
}
4. Cacheable Constraint
Responses must explicitly or implicitly define themselves as cacheable or non-cacheable to prevent clients from reusing stale data.
In Spring, you can implement this using HTTP headers:
@GetMapping("/{id}")
public ResponseEntity getBookById(@PathVariable Long id) {
Book book = bookService.findById(id);
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES))
.body(book);
}
5. Layered System Architecture
A client can't ordinarily tell whether it's connected directly to the end server or to an intermediary. This allows for load balancing, shared caches, and security policies.
In a Book Management API, you might structure your application with these layers:
// Controller Layer - Handles HTTP requests/responses
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
// Service methods
}
// Service Layer - Contains business logic
@Service
public class BookService {
private final BookRepository bookRepository;
// Business logic methods
}
// Repository Layer - Interacts with database
@Repository
public interface BookRepository extends JpaRepository {
// Data access methods
}
// Entity Layer - Models your domain
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
// getters and setters
}
6. Self-Descriptive Methods and Classes
Your API should be intuitive with well-named resources and methods. A developer should be able to understand what a method does just by looking at the URI and HTTP method.
For example, instead of:
@PostMapping("/doSomethingWithBook")
public void executeBookAction(@RequestBody Map data) { }
Use descriptive naming that clearly communicates intent:
@PostMapping
public ResponseEntity createBook(@Valid @RequestBody BookRequest request) {
Book newBook = bookService.saveBook(request);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(newBook.getId())
.toUri();
return ResponseEntity.created(location).body(newBook);
}
@PutMapping("/{id}")
public ResponseEntity updateBook(@PathVariable Long id, @Valid @RequestBody BookRequest request) {
return ResponseEntity.ok(bookService.updateBook(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
return ResponseEntity.noContent().build();
}
The Journey of Continuous Learning
I'm still early in my programming journey and have a long way to go. However, I've realized that continuous learning is paramount to making progress as a developer. True understanding stems from being able to teach others, which is why I decided to write this article! I hope it helps, and happy coding!
Additional Learning Resources
- Microsoft REST API Design Best Practices
- Ambassador's 7 REST API Design Best Practices
- Step-by-Step Guide: Developing a Bookstore API with Spring Boot and MySQL
- Learn API Fundamentals and Architecture – A Beginner-Friendly Guide
- Learn Smarter, AND Work Harder: Insights from ‘Ultralearning’
- make it stick: The Science of successful Learning