Building My First RESTful API: Backend Wizards Stage 0 Journey π
A complete walkthrough of building a dynamic profile endpoint with Java and Spring Boot
π Table of Contents
- The Challenge
- Why Java and Spring Boot?
- Architecture Overview
- Implementation Journey
- Key Learnings
- Challenges Faced
- Testing & Deployment
- Conclusion
π― The Challenge {#the-challenge}
Backend Wizards Stage 0 presented an interesting task: build a RESTful API endpoint that combines static user profile data with dynamic external API integration. The requirements were clear:
-
Endpoint:
GET /me
- Response: JSON with user info, timestamp, and cat fact
- External API: Fetch fresh cat facts from catfact.ninja
- Dynamic Data: New timestamp and cat fact on every request
- Error Handling: Graceful fallbacks for API failures
Here's what the response should look like:
{
"status": "success",
"user": {
"email": "developer@example.com",
"name": "John Developer",
"stack": "Java/Spring Boot"
},
"timestamp": "2025-10-18T12:34:56.789Z",
"fact": "Cats can rotate their ears 180 degrees."
}
π€ Why Java and Spring Boot? {#tech-stack}
While I could have chosen any language, I opted for Java with Spring Boot for several reasons:
Advantages:
β
Industry Standard - Widely used in enterprise applications
β
Spring Boot Magic - Auto-configuration saves tons of boilerplate
β
Strong Typing - Catches errors at compile time
β
Excellent Ecosystem - Mature libraries and tools
β
RestTemplate - Built-in HTTP client with timeout support
β
Easy Deployment - Single JAR file, runs anywhere
Tech Stack:
- Java 17 - Latest LTS version
- Spring Boot 3.2.0 - Web framework
- Maven - Dependency management
- RestTemplate - HTTP client
- Jackson - JSON serialization
- SLF4J - Logging
ποΈ Architecture Overview {#architecture}
I followed a clean MVC architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββ
β ProfileController β β HTTP Layer
β (Handles REST requests/responses) β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β ProfileService β β Business Logic
β (Fetches cat facts, builds model) β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β External Cat Facts API β β External Integration
β (https://catfact.ninja/fact) β
βββββββββββββββββββββββββββββββββββββββ
Layer Breakdown:
1. Controller Layer (ProfileController.java
)
- Handles HTTP requests
- Returns proper status codes
- Manages error responses
2. Service Layer (ProfileService.java
)
- Business logic
- External API calls
- Data transformation
3. DTO Layer (Data Transfer Objects)
-
ProfileResponse
- Main response structure -
UserInfo
- User profile data -
CatFactResponse
- External API response
π» Implementation Journey {#implementation}
Step 1: Setting Up the Project
Created a Spring Boot project with Maven:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
Step 2: Building the Main Application
@SpringBootApplication
public class ProfileApplication {
public static void main(String[] args) {
SpringApplication.run(ProfileApplication.class, args);
}
}
Simple and clean! Spring Boot handles all the heavy lifting.
Step 3: Creating the REST Controller
@RestController
public class ProfileController {
@Autowired
private ProfileService profileService;
@GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getProfile() {
try {
ProfileResponse response = profileService.getProfileWithCatFact();
return ResponseEntity.ok(response);
} catch (Exception e) {
// Error handling
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("status", "error");
errorResponse.put("timestamp", Instant.now().toString());
return ResponseEntity.status(503).body(errorResponse);
}
}
}
Key Points:
-
@RestController
- Combines@Controller
and@ResponseBody
-
@GetMapping
- Maps HTTP GET requests -
ResponseEntity
- Full control over HTTP response - Error handling with proper status codes
Step 4: Implementing the Service Layer
The most interesting part - integrating with the external API:
@Service
public class ProfileService {
private final RestTemplate restTemplate;
public ProfileService(RestTemplateBuilder builder) {
this.restTemplate = builder
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(5))
.build();
}
public ProfileResponse getProfileWithCatFact() {
String catFact = fetchCatFact();
UserInfo userInfo = new UserInfo(email, name, stack);
return new ProfileResponse(
"success",
userInfo,
Instant.now().toString(), // ISO 8601 timestamp
catFact
);
}
private String fetchCatFact() {
CatFactResponse response = restTemplate.getForObject(
"https://catfact.ninja/fact",
CatFactResponse.class
);
return response.getFact();
}
}
Critical Implementation Details:
- Timeout Configuration: 5-second timeout prevents hanging
-
ISO 8601 Timestamps:
Instant.now().toString()
automatically formats - RestTemplate: Spring's HTTP client with automatic JSON mapping
- Constructor Injection: Best practice for dependency injection
Step 5: Creating DTOs
Clean data transfer objects for type safety:
java
public class ProfileResponse {
@JsonProperty("status")
private String status;
@JsonProperty("user")
private UserInfo user;
@JsonProperty("timestamp")
private String timestamp;
@JsonProperty("fact")
private String fact;
// Constructors, getters
Top comments (0)