Module 3: Build Sprint 2

Understanding Your Second Ticket

Learn how to approach your second ticket in the Labs project and understand the development workflow.

Second Ticket Details

View your second ticket details and requirements on GitHub:

Second Ticket Documentation

Approaching Your Second Feature

Learn how to implement JWT authentication and RESTful endpoints for the Bloom Coder Assignment App.

Implementation Checklist

  • Add JWT dependency and implement JWT utilities
  • Create JWT filter for request authentication
  • Update Security Configuration for JWT
  • Implement User Repository
  • Update UserDetailsService implementation
  • Configure application properties
  • Implement RESTful endpoints

JWT Implementation Concepts

// Example of JWT token structure and claims
@Component
public class JwtUtil {
    // Token expiration time in seconds
    private static final long EXPIRATION_TIME = 86400000; // 24 hours
    
    // Secret key for signing tokens
    @Value("${jwt.secret}")
    private String secret;

    // Example of how to structure token claims
    public String generateToken(UserDetails userDetails) {
        // Claims are key-value pairs stored in the token
        Map claims = new HashMap<>();
        
        // Example of adding user roles to claims
        claims.put("roles", userDetails.getAuthorities()
                .stream()
                .map(auth -> auth.getAuthority())
                .collect(Collectors.toList()));

        // Token generation with claims
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }
}

Security Configuration Concepts

// Example of configuring security for JWT authentication
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            // Disable CSRF for API endpoints
            .csrf().disable()
            // Allow specific endpoints without authentication
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            .and()
            // Add exception handling
            .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and()
            // Set session management to stateless
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        
        // Add JWT filter
        httpSecurity.addFilterBefore(
            jwtRequestFilter, 
            UsernamePasswordAuthenticationFilter.class
        );
    }
}

RESTful API Best Practices

Learn about RESTful API design principles and best practices for your Spring Boot application.

API Design Principles

  • Resource-based URLs - Organize around resources, not actions
  • HTTP Methods - Use proper HTTP methods (GET, POST, PUT, DELETE)
  • Status Codes - Return appropriate HTTP status codes
  • Versioning - Consider API versioning strategy
  • Error Handling - Provide clear error messages and codes
  • Documentation - Document your API with Swagger or similar tools

Example REST Controller

@RestController
@RequestMapping("/api/assignments")
public class AssignmentController {
    
    @Autowired
    private AssignmentService assignmentService;
    
    @GetMapping
    public ResponseEntity> getAllAssignments() {
        List assignments = assignmentService.findAll();
        return ResponseEntity.ok(assignments);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity getAssignmentById(@PathVariable Long id) {
        try {
            AssignmentDTO assignment = assignmentService.findById(id);
            return ResponseEntity.ok(assignment);
        } catch (ResourceNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    @PostMapping
    public ResponseEntity createAssignment(
            @Valid @RequestBody AssignmentDTO assignmentDTO) {
        AssignmentDTO created = assignmentService.create(assignmentDTO);
        URI location = ServletUriComponentsBuilder
                .fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(created.getId())
                .toUri();
        return ResponseEntity.created(location).body(created);
    }
    
    @PutMapping("/{id}")
    public ResponseEntity updateAssignment(
            @PathVariable Long id,
            @Valid @RequestBody AssignmentDTO assignmentDTO) {
        try {
            AssignmentDTO updated = assignmentService.update(id, assignmentDTO);
            return ResponseEntity.ok(updated);
        } catch (ResourceNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity deleteAssignment(@PathVariable Long id) {
        try {
            assignmentService.delete(id);
            return ResponseEntity.noContent().build();
        } catch (ResourceNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
}

Error Handling in Spring Boot

Learn about implementing global exception handling and error responses in your Spring Boot application.

Exception Handling Best Practices

  • Global Exception Handler - Use @ControllerAdvice for centralized exception handling
  • Custom Exceptions - Define domain-specific exceptions
  • Error Response Structure - Return consistent error response format
  • Logging - Log exceptions for debugging and monitoring
  • Validation Errors - Handle validation errors consistently

Example Global Exception Handler

@ControllerAdvice
public class GlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity handleResourceNotFoundException(
            ResourceNotFoundException ex, WebRequest request) {
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.NOT_FOUND.value(),
                ex.getMessage(),
                request.getDescription(false),
                new Date()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity handleValidationExceptions(
            MethodArgumentNotValidException ex, WebRequest request) {
        
        Map errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.BAD_REQUEST.value(),
                "Validation Error",
                errors.toString(),
                new Date()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity handleGlobalException(
            Exception ex, WebRequest request) {
        
        logger.error("Unhandled exception", ex);
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                "An unexpected error occurred",
                request.getDescription(false),
                new Date()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}