You are currently viewing Build a Full-Stack CRUD Application with Spring Boot and Angular

Build a Full-Stack CRUD Application with Spring Boot and Angular

Introduction

In today’s rapidly evolving tech landscape, full-stack development has become an essential skill. Combining the power of Spring Boot on the backend with the dynamic capabilities of Angular on the frontend, you can create robust, efficient, and scalable web applications. This tutorial will walk you through building a complete CRUD (Create, Read, Update, Delete) application from scratch. By the end, you’ll have a fully functional web application that allows you to manage a collection of books. Whether you’re a seasoned developer looking to sharpen your skills or a beginner eager to dive into full-stack development, this guide has something for you. Let’s embark on this exciting journey and build something amazing together!

Step 1: Set Up the Backend with Spring Boot

1. Create a new Spring Boot Project

You can use Spring Initializr to create a new Spring Boot project. Include dependencies for Spring Web, Spring Data JPA, and H2 Database.

2. Define Entity Class

Book.java

package com.example.demo.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
    private int year;

    // Constructors, getters, and setters
}

3. Create Repository Interface

BookRepository.java

package com.example.demo.repository;

import com.example.demo.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book, Long> {
}

4. Implement Controller

BookController.java

package com.example.demo.controller;

import com.example.demo.model.Book;
import com.example.demo.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/books")
public class BookController {
    @Autowired
    private BookRepository bookRepository;

    @GetMapping
    public List<Book> getAllBooks() {
        return bookRepository.findAll();
    }

    @PostMapping
    public Book createBook(@RequestBody Book book) {
        return bookRepository.save(book);
    }

    @PutMapping("/{id}")
    public Book updateBook(@PathVariable Long id, @RequestBody Book bookDetails) {
        Book book = bookRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Book not found with id: " + id));
        book.setTitle(bookDetails.getTitle());
        book.setAuthor(bookDetails.getAuthor());
        book.setYear(bookDetails.getYear());
        return bookRepository.save(book);
    }

    @DeleteMapping("/{id}")
    public void deleteBook(@PathVariable Long id) {
        bookRepository.deleteById(id);
    }
}

5. Configure Application Properties

application.properties

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true

6. Main Application Class

DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

7. Project Configuration File

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <!-- Spring Web for RESTful APIs -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Data JPA for Data Access -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- H2 Database for In-memory Database -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Spring Boot DevTools for Development -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- Spring Boot Test for Testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Step 2: Set Up the Frontend with Angular

1. Create a New Angular App

ng new my-crud-app
cd my-crud-app

2. Generate Components

ng generate component book-list
ng generate component add-book
ng generate component edit-book

3. Create a Service

ng generate service book

4. Install Angular HTTP Client Module

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BookListComponent } from './book-list/book-list.component';
import { AddBookComponent } from './add-book/add-book.component';
import { EditBookComponent } from './edit-book/edit-book.component';

@NgModule({
  declarations: [
    AppComponent,
    BookListComponent,
    AddBookComponent,
    EditBookComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 3: Connect Backend with Frontend

1. Configure CORS

CorsConfig.java

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("http://localhost:4200").allowedMethods("*");
            }
        };
    }
}

2. Implement Service Methods

book.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class BookService {
  private baseURL = 'http://localhost:8080/api/books';

  constructor(private http: HttpClient) { }

  getBooks(): Observable<any> {
    return this.http.get(`${this.baseURL}`);
  }

  getBook(id: number): Observable<any> {
    return this.http.get(`${this.baseURL}/${id}`);
  }

  createBook(book: Object): Observable<Object> {
    return this.http.post(`${this.baseURL}`, book);
  }

  updateBook(id: number, value: any): Observable<Object> {
    return this.http.put(`${this.baseURL}/${id}`, value);
  }

  deleteBook(id: number): Observable<any> {
    return this.http.delete(`${this.baseURL}/${id}`, { responseType: 'text' });
  }
}

3. Update Components

book-list.component.ts

import { Component, OnInit } from '@angular/core';
import { BookService } from '../book.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-book-list',
  templateUrl: './book-list.component.html',
  styleUrls: ['./book-list.component.css']
})
export class BookListComponent implements OnInit {
  books: any;

  constructor(private bookService: BookService, private router: Router) { }

  ngOnInit(): void {
    this.reloadData();
  }

  reloadData() {
    this.bookService.getBooks().subscribe(data => {
      this.books = data;
    });
  }

  deleteBook(id: number) {
    this.bookService.deleteBook(id).subscribe(
      data => {
        console.log(data);
        this.reloadData();
      },
      error => console.log(error));
  }

  bookDetails(id: number) {
    this.router.navigate(['details

', id]);
  }

  updateBook(id: number) {
    this.router.navigate(['edit', id]);
  }
}

add-book.component.ts

import { Component, OnInit } from '@angular/core';
import { BookService } from '../book.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-add-book',
  templateUrl: './add-book.component.html',
  styleUrls: ['./add-book.component.css']
})
export class AddBookComponent implements OnInit {
  book: any = {
    title: '',
    author: '',
    year: ''
  };

  constructor(private bookService: BookService, private router: Router) { }

  ngOnInit(): void {
  }

  saveBook() {
    this.bookService.createBook(this.book).subscribe(data => {
      console.log(data);
      this.router.navigate(['/']);
    },
      error => console.log(error));
  }

  onSubmit() {
    this.saveBook();
  }
}

edit-book.component.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BookService } from '../book.service';

@Component({
  selector: 'app-edit-book',
  templateUrl: './edit-book.component.html',
  styleUrls: ['./edit-book.component.css']
})
export class EditBookComponent implements OnInit {
  id: number;
  book: any;

  constructor(private route: ActivatedRoute, private router: Router, private bookService: BookService) { }

  ngOnInit(): void {
    this.book = {};

    this.id = this.route.snapshot.params['id'];

    this.bookService.getBook(this.id)
      .subscribe(data => {
        console.log(data)
        this.book = data;
      }, error => console.log(error));
  }

  updateBook() {
    this.bookService.updateBook(this.id, this.book).subscribe(data => {
      console.log(data);
      this.router.navigate(['/']);
    }, error => console.log(error));
  }

  onSubmit() {
    this.updateBook();
  }
}

Step 4: Run and Test the Application

Backend Testing with cURL

  1. Create a Book:
   curl -X POST -H "Content-Type: application/json" -d '{"title":"Example Book","author":"John Doe","year":2022}' http://localhost:8080/api/books
  1. Get All Books:
   curl http://localhost:8080/api/books
  1. Get a Specific Book by ID:
   curl http://localhost:8080/api/books/{book_id}
  1. Update a Book:
   curl -X PUT -H "Content-Type: application/json" -d '{"title":"Updated Book Title","author":"Jane Doe","year":2023}' http://localhost:8080/api/books/{book_id}
  1. Delete a Book:
   curl -X DELETE http://localhost:8080/api/books/{book_id}

Frontend Testing

  1. Run the Angular Application:
   ng serve
  1. Open your browser and navigate to http://localhost:4200.
  2. Test CRUD Operations:
  • Create Book: Navigate to the “Add Book” page and create a new book.
  • Read Books: View the list of books on the main page.
  • Update Book: Edit a book from the list.
  • Delete Book: Delete a book from the list.

Conclusion

By following this tutorial, you’ve built a full-stack CRUD application using Spring Boot and Angular. This application allows you to create, read, update, and delete books. You’ve learned how to set up a backend with Spring Boot, develop a frontend with Angular, and connect them to perform CRUD operations. You can extend this application further by adding more features such as authentication, pagination, and deployment to a cloud platform. Happy coding!

Leave a Reply