Spring Boot REST API for Mobile Apps: Complete Guide to Building Backend Services
Problem
When I tried to connect a mobile app to a Spring Boot backend, I got this error:
Access to XMLHttpRequest at 'http://localhost:8080/api/user/1' from origin 'https://my-mobile-app.com' has been blocked by CORS policy.This is a common problem mobile developers face when building client-server applications. The browserβs same-origin policy prevents mobile apps from making cross-origin requests to Spring Boot backends.
Environment
- Spring Boot 3.2.0
- Java 21
- Maven 3.9.0
- React Native mobile app
- Development environment with localhost backend
What happened?
I started with a basic Spring Boot controller:
@RestController@RequestMapping("/api")public class UserController {
@GetMapping("/user/{id}") public User getUser(@PathVariable Long id) { User user = userService.findById(id); return user; }}I can explain the key parts:
@RestController: Marks this as a REST controller instead of regular Controller@RequestMapping: Maps the base URL for all endpoints@GetMapping: Handles HTTP GET requests@PathVariable: Extracts ID from URL path
But when I built my mobile app and tried to fetch user data, I got the CORS error:
Access to XMLHttpRequest at 'http://localhost:8080/api/user/1' from origin 'https://my-mobile-app.com' has been blocked by CORS policy.How to solve it?
I tried adding @CrossOrigin to the controller method:
@RestController@RequestMapping("/api")public class UserController {
@GetMapping("/user/{id}") @CrossOrigin(origins = "https://my-mobile-app.com") public User getUser(@PathVariable Long id) { User user = userService.findById(id); return user; }}This worked for the single endpoint, but I need CORS for all my API endpoints. Adding @CrossOrigin to every method would be repetitive.
Then I found a better approach with global CORS configuration:
@Configurationpublic class WebConfig implements WebMvcConfigurer {
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://my-mobile-app.com", "https://ios-app.com", "https://android-app.com") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); }}Now test again:
// Mobile app request succeedsGET /api/user/1Status: 200 OKYou can see that I succeeded to enable CORS for all my mobile API endpoints.
Authentication for Mobile Apps
But I still had security issues. Mobile apps need authentication, so I implemented JWT:
@RestController@RequestMapping("/api/auth")@CrossOrigin(origins = "https://my-mobile-app.com")public class AuthController {
@PostMapping("/login") public ResponseEntity<AuthResponse> login(@RequestBody @Valid LoginRequest request) { User user = authenticate(request.getEmail(), request.getPassword()); String token = jwtUtil.generateToken(user); return ResponseEntity.ok(new AuthResponse(token)); }}The key changes:
@RequestBody @Valid: Validates incoming request dataAuthResponse: Returns JWT token to mobile app- Custom response format for better mobile app handling
JSON Response Optimization
I noticed mobile apps prefer specific response formats. So I created DTOs:
public class UserResponse { private Long id; private String name; private String email; // Getters and setters}
@RestController@RequestMapping("/api/users")public class UserController {
@GetMapping("/{id}") public ResponseEntity<UserResponse> getUser(@PathVariable Long id) { User user = userService.findById(id); UserResponse response = convertToResponse(user); return ResponseEntity.ok(response); }}This separates domain entities from API responses, preventing over-exposure of data.
Pagination for Mobile Performance
Mobile apps handle large data poorly. I added pagination:
@GetMappingpublic ResponseEntity<Page<UserResponse>> getUsers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { Page<User> users = userService.findAll(PageRequest.of(page, size)); Page<UserResponse> responses = users.map(this::convertToResponse); return ResponseEntity.ok(responses);}Mobile apps can now request smaller chunks of data:
GET /api/users?page=0&size=10Response: { "content": [...], "totalElements": 100, "totalPages": 10, "size": 10}Error Handling for Mobile Apps
Generic error messages frustrate mobile developers. I created proper error responses:
@RestControllerAdvicepublic class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) { ErrorResponse error = new ErrorResponse( "USER_NOT_FOUND", ex.getMessage(), "The requested user could not be found" ); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error); }
@ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) { ErrorResponse error = new ErrorResponse( "INTERNAL_ERROR", "An unexpected error occurred", "Please try again later" ); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); }}Now mobile apps get consistent error formats they can handle:
{ "code": "USER_NOT_FOUND", "message": "User with ID 999 not found", "userMessage": "The requested user could not be found"}API Versioning
When I needed to update my API, I worried about breaking mobile apps. So I implemented versioning:
@RestController@RequestMapping("/api/v1/users")@CrossOrigin(origins = "https://my-mobile-app.com")public class V1UserController { // Version 1 endpoints}
@RestController@RequestMapping("/api/v2/users")@CrossOrigin(origins = "https://my-mobile-app.com")public class V2UserController { // Version 2 endpoints with new features}Mobile apps can use the old API while I develop new features:
GET /api/v1/users/1 // Old mobile appGET /api/v2/users/1 // New mobile appThe reason
I think the key reason for these errors and solutions are:
- CORS is necessary because browsers enforce same-origin policy for security, but mobile apps need to connect to different backends
- JWT authentication provides stateless security perfect for mobile apps that can store tokens securely
- DTOs separate concerns by keeping domain models clean while controlling what mobile apps see
- Pagination prevents mobile apps from downloading more data than they can handle
- Error responses follow consistent patterns that mobile apps can parse and handle
- API versioning allows gradual migration without breaking existing mobile clients
Summary
In this post, I showed how to build a Spring Boot REST API backend for mobile applications. The key point is implementing proper CORS configuration, JWT authentication, JSON response formatting, and mobile-specific features like pagination and consistent error handling.
Spring Boot makes an excellent choice for mobile backends because it automatically handles JSON serialization with Jackson, provides easy CORS configuration, and offers comprehensive security features. By following these patterns, you can create scalable backends that work seamlessly with iOS, Android, and React Native mobile applications.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- π¨βπ» Spring Boot Documentation
- π¨βπ» Mobile Backend Best Practices
- π¨βπ» REST API Design Guide
- π¨βπ» JWT Authentication Tutorial
- π¨βπ» CORS Configuration
Oh, and if you found these resources useful, donβt forget to support me by starring the repo on GitHub!
Comments