RestAPI using Java
Tags: java, beginners, programming, mongodb
Published: 2022-11-27 16:52:23

Hello everyone, today we'll look at how we can use Java and Spring Boot to create a simple book API that performs basic CRUD operations. We will also use MongoDb, a NoSQL database, to store the data. 

![started](https://media.giphy.com/media/5zf2M4HgjjWszLd4a5/giphy.gif)

#### Prerequisite
🎯 Integrated Development Environment (IntelliJ IDEA recommended)
🎯 Have Java installed on your computer
🎯 Java fundamentals
🎯 Postman for API testing
🎯 MongoDB Atlas or MongoDB Compass 

---

#### ☕ Project Setup
Go to the Spring initializr website and select the necessary setup. [Link](https://start.spring.io/)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3fvpbu6sq43ksh0777ys.png)

Next, unzip the folder and open it with your IDE, in my case IntelliJ, and create a project out of the `pom.xml` file. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d08otpatokk2setdhm8s.png)


---

Let's quickly change our port number to '3456'.
You can use the default, '8080,' but that port is already in use by another service on my machine.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swxnprwj92zwqiys8fx8.png)

---

#### Starting your application
To begin, navigate to your main entry file, in my case the `YomiApplication` class, and click the green play button highlighted below. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/itx44kfmqratu6dvqn2q.png)

After a successful build, your server should be launched at the port we specified earlier. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gn65qixpp0xrm6dqzpa6.png)


---

#### Packages
Let's now create packages to modularize our application. 
> A package in Java is used to group related classes. Think of it as a folder in a file directory. We use packages to avoid name conflicts, and to write a better maintainable code.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubpmsfaabqbt3vabc9yo.png)

---

### Model
Let's create our book entity, or the fields that our Book model will have.
A model class is typically used in your application to "shape" the data.
For example, you could create a Model class that represents a database table or a JSON document. 

Create a Book Class within the model package and write the code snippet below. 
```java
package com.example.yomi.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;

@Setter //@Setter is a Lombok annotation that generates setter methods for all fields in the class.
@Getter //@Getter is a Lombok annotation that generates getter methods for all fields in the class.
@AllArgsConstructor //@AllArgsConstructor is a Lombok annotation that generates a constructor with all fields in the class.
@NoArgsConstructor  //@NoArgsConstructor is a Lombok annotation that generates a constructor with no arguments.
@Document(collection = "books") // @Document annotation is used to specify the collection name (MongoDB collection name)
public class Book {
    @Id
    private String id; // private means only this class can access this field

    private String name;

    private String title;

    private Boolean published;

    private String author;

    private Date createdAt;

    private Date updatedAt; // Date is a class in Java that represents a date and time
}


```

---

#### Book service
Let's move on to the service now that we've structured our Book A client will use a Service class to interact with some functionality in your application.

We will create a BookService Interface and a BookServiceImplementation Class in the `service package.`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njc5fk3gnfgsuubqt9n7.png)

We will implement a method to create a single book in the `BookService` interface. 

```java
package com.example.yomi.service;

import com.example.yomi.model.Book; // we import the Book model class

public interface BookService { // interface is a contract that defines the behavior of a class
    public void createBook(Book book); // the `book` in lowercase is the name of the parameter that we will pass to the method
    // the `Book` in uppercase is the name of the class that we will pass to the method
}

```

---

Let us now put this createBook method into action in the `BookServiceImpl` class
```java
package com.example.yomi.service;

import com.example.yomi.model.Book;
import com.example.yomi.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service  // this annotation tells Spring that this class is a service
public class BookServiceImpl implements BookService{ // we implement the BookService

    @Autowired // this annotation tells Spring to inject the BookRepository dependency
    private BookRepository bookRepository; // we inject the BookRepository dependency

    public void createBook(Book book) {
        book.setCreatedAt(new Date(System.currentTimeMillis()));
        bookRepository.save(book);
    }


}

```

Before proceeding, we must connect our application to MongoDB.
We will proceed in two stages. 

1)
Enter the mongodb configuration in the `application.properties` file. 

```java
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/book-app
spring.data.mongodb.database=book-app
```

2) In the book repository file `BookRepository`
```java
package com.example.yomi.repository;

import com.example.yomi.model.Book;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

// Repository should be created as an interface
@Repository
public interface BookRepository extends MongoRepository<Book, String> {
    
//    we extend the MongoRepository interface and pass the Book model class and the type of the id field
}

```

---

Let's finish our 'BookServiceImpl' that we started earlier. 
```java
package com.example.yomi.service;

import com.example.yomi.model.Book;
import com.example.yomi.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service  // this annotation tells Spring that this class is a service
public class BookServiceImpl implements BookService{ // we implement the BookService 
    
    @Autowired // this annotation tells Spring to inject the BookRepository dependency
    private BookRepository bookRepository; // we inject the BookRepository dependency
    
    public void createBook(Book book) {
        bookRepository.save(book);
    }
    

}
```

---

Before we can test, we must first create our API controller.
Let's make a `BookController` class in the controller package.
 
```java
package com.example.yomi.controller;

import com.example.yomi.model.Book;
import com.example.yomi.repository.BookRepository;
import com.example.yomi.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController // this annotation tells Spring that this class is a controller
public class BookController {

    @Autowired // this annotation tells Spring to inject the BookRepository dependency
    private BookRepository bookRepository; // we inject the BookService dependency

    @Autowired // this annotation tells Spring to inject the BookService dependency
    private BookService bookService; // we inject the BookService dependency

    @PostMapping("/books") // this annotation tells Spring that this method is a POST request
    public ResponseEntity<?> createBook(@RequestBody Book book) { // the ? means that the return type is a generic type
        bookService.createBook(book);
        bookRepository.save(book);
        return new ResponseEntity<>(book, HttpStatus.CREATED); //ResponseEntity is a generic type that returns a response
    }
}

```

#### After restarting your app, use Postman to test the Create Book API. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhgng5x28kmojgmo2fhg.png)

We get the document uploaded in the mongo compass when we check the database. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmm1hlolrlrxd48czj5x.png)

---

Let's take a step back and look at the application's connection chain.
In this order, we have the model, repository, service, implementation, database, and client. 

```java
model -> repository -> service -> serviceImplementation -> controller -> database -> client
```

---

### Let's now make the get all books request.
Let's make a request for all of the books.
We add a new method called `getAllbooks` to the `BookController.` 
```java
//...
 @GetMapping("/books")
    public ResponseEntity<?> getAllBooks() { // the ? means that the return type is a generic type
       List<Book> books =  bookRepository.findAll();
         return new ResponseEntity<>(books, books.size() > 0 ? HttpStatus.OK: HttpStatus.NOT_FOUND ); //ResponseEntity is a generic type that returns a response
        // books.size() means that if the size of the books list is greater than 0, return HttpStatus.OK, else return HttpStatus.NOT_FOUND
    }
//...
```

In `BookService` we create a new method
```java
//..
 List<Book> getAllBooks();
//...
```
Let's put the getAllBooks method into action by right-clicking on the BookServiceImpl. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xeg5hacts663ddycyrxa.png)

And then select methods to implement. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ab2xnj4f5n739apw5ard.png)

Then we call the bookRepository.findAll();
```java
@Override
    public List<Book> getAllBooks() {
        List<Book> books = bookRepository.findAll();
        return books;
        
    }
```

Let us restart the server and run some tests in Postman. We get:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mskiksvn96zcani5drbr.png)

Let's add a new book just to make sure we have enough documents. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/03u5d4ict35oc0gou9ps.png)

Let's retry the read all with the GET request. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvixfh86uh7fg4e8so30.png)

We now have two documents in our database. 


---


We are almost done. Hang in there
![image](https://media.giphy.com/media/YT95XJOLvY1t2SJgpR/giphy.gif)
Let's use the id to create a get single book, delete it, and update it.
All we need to do is edit the controller, bookService, and bookServiceImplementation files in the same format.

I'll include a link to the source code as well as the entire set of snippets.

Simply follow the same steps as we did for the Post and Get requests. 


```java
//BookController
package com.example.yomi.controller;

import com.example.yomi.model.Book;
import com.example.yomi.repository.BookRepository;
import com.example.yomi.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController // this annotation tells Spring that this class is a controller
public class BookController {

    @Autowired // this annotation tells Spring to inject the BookRepository dependency
    private BookRepository bookRepository; // we inject the BookService dependency

    @Autowired // this annotation tells Spring to inject the BookService dependency
    private BookService bookService; // we inject the BookService dependency

    @PostMapping("/books") // this annotation tells Spring that this method is a POST request
    public ResponseEntity<?> createBook(@RequestBody Book book) { // the ? means that the return type is a generic type
        bookService.createBook(book);
        bookRepository.save(book);
        return new ResponseEntity<>(book, HttpStatus.CREATED); //ResponseEntity is a generic type that returns a response
    }

    @GetMapping("/books")
    public ResponseEntity<?> getAllBooks() { // the ? means that the return type is a generic type
       List<Book> books =  bookRepository.findAll();
         return new ResponseEntity<>(books, books.size() > 0 ? HttpStatus.OK: HttpStatus.NOT_FOUND ); //ResponseEntity is a generic type that returns a response
        // books.size() means that if the size of the books list is greater than 0, return HttpStatus.OK, else return HttpStatus.NOT_FOUND
    }

    @GetMapping("/books/{id}")
    public ResponseEntity<?> getBookById(@PathVariable("id") String id) { // the ? means that the return type is a generic type
      try {
          Book book = bookService.getBookById(id);
          return new ResponseEntity<>(book, HttpStatus.OK); //ResponseEntity is a generic type that returns a response
      } catch (Exception e) {
          return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
      }
    }

    @PutMapping("/books/{id}")
    public ResponseEntity<?> updateBookById(@PathVariable("id") String id, @RequestBody Book book) {
        try {
            bookService.updateBook(id, book);
            return new ResponseEntity<>("Updated Book with id "+id, HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); //ResponseEntity is a generic type that returns a response
        }
    }

    @DeleteMapping("/books/{id}")
    public ResponseEntity<?> deleteBookById(@PathVariable("id") String id) {
        try {
            bookService.deleteBook(id);
            return new ResponseEntity<>("Deleted Book with id "+id, HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); //ResponseEntity is a generic type that returns a response
        }
    }
}

```

---

```java
//BookService
package com.example.yomi.service;

import com.example.yomi.model.Book; // we import the Book model class

import java.util.List;

public interface BookService { // interface is a contract that defines the behavior of a class
    public void createBook(Book book);
    // the `book` in lowercase is the name of the parameter that we will pass to the method
    // the `Book` in uppercase is the name of the class that we will pass to the method

    List<Book> getAllBooks();

    public Book getBookById(String id);

    public void updateBook(String id, Book book);

    public void deleteBook(String id);

}

```

---

BookServiceImpl
```java
package com.example.yomi.service;

import com.example.yomi.model.Book;
import com.example.yomi.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;

@Service  // this annotation tells Spring that this class is a service
    public class BookServiceImpl implements BookService{ // we implement the BookService

    @Autowired // this annotation tells Spring to inject the BookRepository dependency
    private BookRepository bookRepository; // we inject the BookRepository dependency

    public void createBook(Book book) {
        book.setCreatedAt(new Date(System.currentTimeMillis()));
        bookRepository.save(book);
    }

    @Override
    public List<Book> getAllBooks() {
        List<Book> books = bookRepository.findAll();
        return books;

    }

    @Override
    public Book getBookById(String id) {
        Book book = bookRepository.findById(id).get();
        return book;
    }

    @Override
    public void updateBook(String id, Book book) {
        Book bookToUpdate = bookRepository.findById(id).get();
        bookToUpdate.setTitle(book.getTitle());
        bookToUpdate.setAuthor(book.getAuthor());
        bookToUpdate.setCreatedAt(book.getCreatedAt());
        bookToUpdate.setUpdatedAt(new Date(System.currentTimeMillis()));
        bookRepository.save(bookToUpdate);
    }

    @Override
    public void deleteBook(String id) {
        bookRepository.deleteById(id);
    }


}

```

---

##### Update post

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7cyoq0kgude09evy0fg.png)

Result

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0jsa4mfv83x12cy8t6t.png)



#### Get Single Book using the ID

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/or9frv7y9olylyres0ux.png)


##### Delete book using Id

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3ogj828a2ab5nvml6ei.png)

#### Conclusion
I understand that this is a lengthy read. Give yourself enough time to read between the lines.
There are some topics that are not covered, such as input validation and entity association.
I am hoping to continue in the near future. Keep an eye out.

Continue to learn, and may the force be with you. Peace

#### Resources
[Source code](https://github.com/drsimplegraffiti/book-api)
[Spring boot and MongoDB REST API CRUD Tutorial](https://www.youtube.com/watch?v=WUMIplH9_NE&list=PLA7e3zmT6XQUjrwAoOHvNu80Axuf-3jft)