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
- 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
- Get All Books:
curl http://localhost:8080/api/books
- Get a Specific Book by ID:
curl http://localhost:8080/api/books/{book_id}
- 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}
- Delete a Book:
curl -X DELETE http://localhost:8080/api/books/{book_id}
Frontend Testing
- Run the Angular Application:
ng serve
- Open your browser and navigate to
http://localhost:4200
. - 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!