Creating a Hello World REST API

hitarth's Vault // Exported at - 10:14 PM

// REST API
@RestController
public class HelloWorldController {

    @RequestMapping(method = RequestMethod.GET, path = "/hello-world")
    public String helloWorld() {
        return "Hello World";
    }
}

another better way of doing this is using the @GetMapping :

// REST API
@RestController
public class HelloWorldController {

    @GetMapping(path = "/hello-world")
    public String helloWorld() {
        return "Hello World";
    }
}

Enhancing the Hello world REST API to return a Bean : JSON Response is returned

Pasted image 20240210145200.png
Pasted image 20240210145208.png
Pasted image 20240210145218.png

Using path variables :

Pasted image 20240210150122.png

// path parameters

    @GetMapping(path = "hello-world/path-variable/{name}")
        public HelloWorldBean helloWorldPathVariable(@PathVariable String name) {
            return new HelloWorldBean("Path Variable: " + name);
        }

Pasted image 20240210150149.png

Using String.format :
Pasted image 20240210150313.png
Pasted image 20240210150322.png

Request Methods for REST API :

Pasted image 20240210150724.png

Social Media REST API Mini Project :

Pasted image 20240210150944.png

Pasted image 20240210151013.png

  • Use plurals, they are much easier to read and understand :

First we create the User object :
Pasted image 20240210151729.png

This is a user bean, we want to build a REST API around this user bean. Now, to be able to perform operations on something that is stored in the data base we'll create the DAO Object i.e Data Access Object.

  • Pasted image 20240210155505.png

To manage the resources, we'll create another file named UserResorce.java

  • Pasted image 20240210160045.png
  • Output :
    Pasted image 20240210160101.png

Filtering by User ID :

@GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable int id) {
        return service.retrieveUser(id);
    }
public User retrieveUser( int id) {

        // either use for loop to iterate through the list and find by id
        // or use functional programming :
        Predicate<? super User> predicate = user -> user.getId().equals(id);
        return users.stream().filter(predicate).findFirst().get();

    }

Creating Users : POST

We'll create a usersCount variable to add the ID automatially.

UserDaoService.java

package org.example.rest.webservices.restfulwebservices.user;

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

@Component // since we want spring to manage it therefore we annotate the class as a component
public class UserDaoService {

    private static List<User> users = new ArrayList<>();

    private static int usersCount = 0;

    static {
        users.add(new User(++usersCount, "Adam", LocalDate.now()));
        users.add(new User(++usersCount, "Eve", LocalDate.now()));
        users.add(new User(++usersCount, "Jim", LocalDate.now()));
    }

    public List<User> findAll() {
        return users;
    }

    public User retrieveUser(int id) {

        // either use for loop to iterate through the list and find by id
        // or use functional programming :
        Predicate<? super User> predicate = user -> user.getId().equals(id);
        return users.stream().filter(predicate).findFirst().get();

    }

    public User save(User user) {
        user.setId(++usersCount);
        users.add(user);
        return user;
    }

}
@PostMapping("/users")
    public void createUser(@RequestBody User user) {
        service.save(user);
    }
public User save(User user) {
        user.setId(++usersCount);
        users.add(user);
        return user;
    }

Response Statuses for REST API :

Pasted image 20240210181605.png

@PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
         service.save(user);

        return ResponseEntity.created(null).build(); // returns a status of 201 created, .build() builds the response entity object
    }

Pasted image 20240210182000.png

  • Now we can see that the response status is 201

Returning URI of the newly created user :

@PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
         service.save(user);

        // /users/4 => /users  /{id} => user.getId()
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}") // to append the id to the current request
                .buildAndExpand(user.getId()) // to replace the {id} with the actual id
                .toUri(); // to convert it to a proper URI
        return ResponseEntity.created(location).build(); // returns a status of 201 created, .build() builds the response entity object
    }

Pasted image 20240210191136.png

  • We can see the URI here now in the location header.

Exception Handling : 404 Not Found :

public User retrieveUser(int id) {

        // either use for loop to iterate through the list and find by id
        // or use functional programming :
        Predicate<? super User> predicate = user -> user.getId().equals(id);
        return users.stream().filter(predicate).findFirst().get();
		
    }

in the code above, if there is no element of a given ID then get() throws an NoSuchElementException error. If we want to return null value when no ID exists we can replace that line with : return users.stream().filter(predicate).findFirst().orElse(null);
However this is not good as well because we are still returning something which is null and the status code will be 200. We'll have to fix that :

UserResource.java

package org.example.rest.webservices.restfulwebservices.user;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;
import java.util.List;

@RestController
public class UserResource {

    private UserDaoService service;

    public UserResource(UserDaoService service) {
        this.service = service;
    }

    @GetMapping("/users")
    public List<User> retrieveAllUsers() {
        return service.findAll();
    }

    @GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable int id) {
        User retrievedUser = service.retrieveUser(id);
        if (retrievedUser == null) {
            throw new UserNotFoundException("id: " + id);
        }
        else {
            return retrievedUser;
        }
    }

    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
         service.save(user);

        // /users/4 => /users  /{id} => user.getId()
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}") // to append the id to the current request
                .buildAndExpand(user.getId()) // to replace the {id} with the actual id
                .toUri(); // to convert it to a proper URI
        return ResponseEntity.created(location).build(); // returns a status of 201 created, .build() builds the response entity object
    }

}

UserNotFoundException.java

package org.example.rest.webservices.restfulwebservices.user;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.NOT_FOUND) // to return a status of 404 not found, by default it returns 500 internal server error
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
            super(message); // pass the message to the super class, which is the RuntimeException
    }
}

Pasted image 20240210194532.png

  • We now get a proper response back.

Custom JSON Exception Structure :

Video hi dekh lo bhai
Pasted image 20240210201917.png
Pasted image 20240210201932.png
Pasted image 20240210202339.png

If for UserNotFoundException we wish to return a 400 bad request then we'll do this
Pasted image 20240210202710.png

Delete User :

@DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable int id) {
        User retrievedUser = service.retrieveUser(id);
        if (retrievedUser == null) {
            throw new UserNotFoundException("id: " + id);
        }
        else {
            service.deleteById(id);
        }
    }
public void deleteById(int id) {
        Predicate<? super  User> predicate = user -> user.getId().equals(id);
        users.removeIf(predicate);
    }

Validations for REST API

  • We'll be using jakarta validation constraints for setting up constraints in the User class.
  • We'll also use the following dependency to validate the constraints
<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-validation</artifactId>
		<scope>runtime</scope>
		<optional>true</optional>
</dependency>
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {

//        ErrorDetails errorDetails = new ErrorDetails(LocalDate.now(), ex.getMessage(), request.getDescription(false));

        ErrorDetails errorDetails = new ErrorDetails(LocalDate.now(), ex.getFieldError().getDefaultMessage(), request.getDescription(false)); // → to return the message of the validation error



        return new ResponseEntity(errorDetails, HttpStatus.BAD_REQUEST);
    }
}

Pasted image 20240210223139.png

Swagger Documentation :

<dependency>
	<groupId>org.springdoc</groupId>
	<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
	<version>2.3.0</version>
</dependency>

Versioning REST API :

Pasted image 20240212003450.png

URI Versioning :

  • We use different URLs for different versions :

Pasted image 20240212012441.png
Pasted image 20240212012449.png
Pasted image 20240212012458.png
Pasted image 20240212012507.png

Pasted image 20240212012521.png
Pasted image 20240212012543.png

Serialization : Conversion of Objects to JSON :

skipped