Search⌘ K
AI Features

Wishlist Feature

Explore how to implement a wishlist feature in a full-stack e-commerce app backend using Java and Spring Boot. Learn to model wishlist entities, create repositories, services, and APIs with user authentication. Understand how to save and retrieve wishlist items for individual users, ensuring a personalized shopping experience.

A wishlist is an e-commerce feature that allows shoppers to create personalized collections of products that they want to buy in the future and save in their user accounts.

Model

We’ll have one wishlist per user. So the Wishlist entity will have a one-to-one relationship with the user entity.

Each wishlist can have multiple products. So the Wishlist entity will have a many-to-one relationship with the product entity.

The Wishlist also have an auto-generated id field, as well as a created_date field. We can sort the items by the created_date field.

C++
package com.educative.ecommerce.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.util.Date;
@Entity
@Table(name = "wishlist")
public class WishList {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
@JoinColumn(nullable = false, name = "user_id")
private User user;
@Column(name = "created_date")
private Date createdDate;
@ManyToOne()
@JoinColumn(name = "product_id")
private Product product;
public WishList() {
}
public WishList(User user, Product product) {
this.user = user;
this.product = product;
this.createdDate = new Date();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}

If we run the code and open the MySQL terminal, we’ll see that a wishlist table has been created.

Repository

Here, we’ll create a repository for the model.

The repository will have a method to find every product added to the wishlist, which will be sorted by date created.

Java
@Repository
public interface WishListRepository extends JpaRepository<WishList, Integer> {
// find all the products saved to wishlist for a user
// sort by latest created date
List<WishList> findAllByUserOrderByCreatedDateDesc(User user);
}

Service

Here, we’ll create WishListService class in the service package. It will autowire WishListRepository.

It will have two simple methods to save a wishlist and read all the wishlist items for a user.

C++
@Service
public class WishListService {
@Autowired
private WishListRepository wishListRepository;
public void createWishlist(WishList wishList) {
wishListRepository.save(wishList);
}
public List<WishList> readWishList(User user) {
return wishListRepository.findAllByUserOrderByCreatedDateDesc(user);
}
}

Controller

As we have our service, model, and repository ready, we’ll start building the APIs.

Create a class, WishListController, in the controllers package, which will autowire wishListService and AuthenticationService. We’ll use these to retrieve the user. 1

Java
@Controller
public class WishListController {
@Autowired
WishListService wishListService;
@Autowired
AuthenticationService authenticationService;
@Autowired
private ProductRepository productRepository;
}

Add to wishlist API

We will have a request body, product, as well as a RequestParam token, which is the authentication token we generated and returned to the user during sign-in.

First, we’ll verify if the token is valid. Then, we’ll retrieve the user and save the item to the wishlist.

C++
/*
API to add a new product in wishlist
*/
/*
API to add a new product in wishlist
*/
@PostMapping("/add")
public ResponseEntity<ApiResponse> addWishList(@RequestBody ProductDto productDto, @RequestParam("token") String token) throws AuthenticationFailException {
// first authenticate if the token is valid
authenticationService.authenticate(token);
// then fetch the user linked to the token
User user = authenticationService.getUser(token);
// get the product from product repo
Product product = productRepository.getById(productDto.getId());
// save wish list
WishList wishList = new WishList(user, product);
wishListService.createWishlist(wishList);
return new ResponseEntity<ApiResponse>(new ApiResponse(true, "Added to wishlist"), HttpStatus.CREATED);
}

Test

Next, we’ll test if the saving feature of the wishlist API is working.

We’ll sign in and retrieve authenticationToken.

We’ll use this token to hit the /wishlist/add API with productDTO.

We’ll get a successful response with the following message: Added to Wishlist.

We can also verify in the database that we have added the product to the wishlist.

Get the wishlist API

Now, that we can save products in the Wishlist, it’s time to retrieve the Wishlist for the user.

Java
@GetMapping("/{token}")
public ResponseEntity<List<ProductDto>> getWishList(@PathVariable("token") String token) throws AuthenticationFailException {
// first authenticate if the token is valid
authenticationService.authenticate(token);
// then fetch the user linked to the token
User user = authenticationService.getUser(token);
// first retrieve the wishlist items
List<WishList> wishLists = wishListService.readWishList(user);
List<ProductDto> products = new ArrayList<>();
for (WishList wishList : wishLists) {
// change each product to product DTO
products.add(new ProductDto(wishList.getProduct()));
}
// send the response to user
return new ResponseEntity<>(products, HttpStatus.OK);
}

Let’s hit the API in swagger and test it.

The final code

/*
 * Copyright 2007-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;

public class MavenWrapperDownloader {

    private static final String WRAPPER_VERSION = "0.5.6";
    /**
     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
     */
    private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
        + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";

    /**
     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
     * use instead of the default one.
     */
    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
            ".mvn/wrapper/maven-wrapper.properties";

    /**
     * Path where the maven-wrapper.jar will be saved to.
     */
    private static final String MAVEN_WRAPPER_JAR_PATH =
            ".mvn/wrapper/maven-wrapper.jar";

    /**
     * Name of the property which should be used to override the default download url for the wrapper.
     */
    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";

    public static void main(String args[]) {
        System.out.println("- Downloader started");
        File baseDirectory = new File(args[0]);
        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());

        // If the maven-wrapper.properties exists, read it and check if it contains a custom
        // wrapperUrl parameter.
        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
        String url = DEFAULT_DOWNLOAD_URL;
        if(mavenWrapperPropertyFile.exists()) {
            FileInputStream mavenWrapperPropertyFileInputStream = null;
            try {
                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
                Properties mavenWrapperProperties = new Properties();
                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
            } catch (IOException e) {
                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
            } finally {
                try {
                    if(mavenWrapperPropertyFileInputStream != null) {
                        mavenWrapperPropertyFileInputStream.close();
                    }
                } catch (IOException e) {
                    // Ignore ...
                }
            }
        }
        System.out.println("- Downloading from: " + url);

        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
        if(!outputFile.getParentFile().exists()) {
            if(!outputFile.getParentFile().mkdirs()) {
                System.out.println(
                        "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
            }
        }
        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
        try {
            downloadFileFromURL(url, outputFile);
            System.out.println("Done");
            System.exit(0);
        } catch (Throwable e) {
            System.out.println("- Error downloading");
            e.printStackTrace();
            System.exit(1);
        }
    }

    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
        if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
            String username = System.getenv("MVNW_USERNAME");
            char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
            Authenticator.setDefault(new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });
        }
        URL website = new URL(urlString);
        ReadableByteChannel rbc;
        rbc = Channels.newChannel(website.openStream());
        FileOutputStream fos = new FileOutputStream(destination);
        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        fos.close();
        rbc.close();
    }

}
Final code