How to Build a RESTful CRUD App With Spring Boot and Angular

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Welcome back! Today I will build a CRUD application using Spring Boot 3 and Angular 16. I will create both projects from scratch, the backend and the frontend. And I will do it using the RESTful endpoints. But what are the CRUD operations? CRUD stands for Ccreate, Read, Update and Delete. It's a set of basic operations that are essential for any back office application. Okay, and what are the RESTful endpoints? The RESTful endpoints follow a standard. It describes how to build an URL. It impacts the HTTP method, the words used in the URL, the variables, the order, the parameters, and the body. And now, how do I use the RESTful endpoints for my CRUD application? The basic CRUD operations are Create, Read, Update and Delete. The easiest one is the Read. Read them all. And now, I have to adapt my endpoint to the resource I want. It could be hotels, books or vehicles. Let's stay with the vehicles. The first level is in plural, because I want them all. And if I want to read a single element? I use the ID. For the creation I need now to change the verb. I use POST when I want to upload some content and save it in the application. Which content do I want to upload? A vehicle. And it returns me the vehicle I've created. Let's continue with the Delete. There is already an HTTP method which indicates the deletion. Let's use it. And the URL must tell the resource I want to delete with its ID. This usually returns the deleted resource. And the last, the update. I can do the update with the POST method, the PUT method, and the PATCH method. But in fact, the POST method is not very suitable. The definition tells that the result in the database will be different if I perform many times the same request. So I have the PUT and the PATCH. Which one to choose? The definition of the PUT is replace entirely. If I upload a vehicle with PUT, it will replace all the fields, and the missing fields will be set to null. And if I use PATCH, by definition, it will only replace the given fields and ignore the missing ones. I will now Implement a simple application with a frontend in angular and the backend in Spring Boot. I will create the CRUD endpoints in the backend, and a form in the frontend, to Create, Read, Update and Delete vehicles from my database. Let's do it. Let's start by creating the backend with Spring Initializer. I start by editing the group, the artifact, and the name. I select Maven. I only need the web dependency for the endpoints, JPA to store the data in the database, and the Postgres driver. I will also add Lombok for the code generation. Now I download it and open it with IntelliJ. I will now add another dependency, Mapstruct, which is unavailable in Spring Initializer. Let's open the pom XML. And at the end of the dependencies, I will start by adding Mapstruct. And the processor. I also comment the JPA and Postgres driver. I will configure the database later. Step by step. Let's now create my first endpoint. I first create the controllers package. And the vehicle controller. RestController annotation. And the GetMapping, as it is a GET endpoint. It will return a ResponseEntity with a list of vehicles. I will create the DTO later. And return to ResponseEntity.ok from a service. Let's start by adding the service. And the constructor from Lombok. Now the DTO. With the fields Id, brand, model, color. And here let's add some Lombok annotations for the constructors, builder, some data methods, getters and setters. I will add the NotNull annotation to make sure those fields are not null. But I need the validation dependency. Let's fix the import. And create the service in the services package. Service annotation. Method all vehicles. I will create a final list with some fixed vehicles. Ford Mondeo and a Citroen C2. Let's test now the endpoint. Here are the vehicles. Let's have another endpoint to get a single element. This time I need a variable in the URL, the Id. A GET again. But now with a variable. To get the value of the variable I need the PathVariable annotation. And return a ResponseEntity with the response of the service again. Let's go to the service. get vehicle. I create a stream from the list and filter the vehicle with the Id I request. I find the first or throw an exception. With an HTTP status NotFound. When no vehicle is found, I throw an exception. Returning null is a bad idea, instead, with an exception I can intercept it with an aspect and return an adequate message to the frontend. For the exception, I create the exceptions package. Just the message and the status. And it must extends the RuntimeException. I save the status and create the getter. And now let's create the aspect. Create the config package. RestExceptionHandler, the ControllerAdvice annotation. It also returns a ResponseEntity from an ErrorDto object. I will create it later. And I want to intercept only the AppException. I set the status manually, and the body from the message. I create the DTO as a record. Let's test this new endpoint. Oh it's ok not of. Let's test again. I get the first vehicle. Oh I have the Id 1 twice. Now it's working. Okay, I have the Read endpoint. Let's continue with the Create endpoints. And now I will also configure the database connection. I uncomment the JPA and the driver. And now it's a POST method to the vehicles. Create a vehicle. I add the Valid annotation to validate the NotNull annotations. And a body which is a vehicle. I return a ResponseEntity with a Created. The Created must return a URL with the created resource. And now create the vehicle in the service. Let's first configure the database connection. I will use a yaml file. I set the driver. The URL of my database. Username and password. The dialect of the JPA. And configure Hibernate to create and drop my database upon the entities I've created. Let's now create the entities package and the vehicle entity. The Entity and the Table annotation. And the same Lombok annotations. And now the same fields. Id, brand, model, color and year. The Id must contain the Id annotation. The rest, the column annotations. I want my Id to be generated with a sequence. Now the repository to access the database. It's an interface which extends JpaRepository. It returns vehicle and the Id is a long. And now the Mapstruct mappers. VehicleMapper. The Mapper annotation, to be injectable by Spring. And create a method to map a VehicleDto to a Vehicle. And another one to map a Vehicle to a VehicleDto. I'll also create a list. Let's create now the method in the service. I must inject the repository and the mapper. I delete the hard-coded list. Now I can update the all vehicles method with the repository result. I must map to the Dto. And the get one vehicle. And use the mapper. When creating the vehicle, I first map the Dto into an entity. And save the entity. Finally, I return the created object. Let's test now this last endpoint. Oh I missed the semicolon. curl Now I use the POST method. My application only accepts Json content. And now the data. The brand, model, color and year. Let's create a Ferrari. And here it is. I'm able to read and create vehicles from my database with my backend. Let's turn now to the frontend. Create the project and display all this. I will now create an Angular project. NG new frontend. The default routing. And the CSS style. I will now add only one dependency, materials. To easily create UI components. NG add material. Yes, the latest version. And choose the default theme. Yes, default values. And now I can create my first component. The one to display a single vehicle. NG generate component vehicle display. Now when I load the main component, the app component, I want my frontend to fetch all the vehicles. In the main component, I will save all the vehicle received from my backend. I will create the structure of a vehicle in a class. With all the fields. Id, brand, model, color and year. I import the vehicle. I inject the HTTP client to request my backend. When initializing the component, I request all the vehicles for my backend. In the subscribe, I save the response in the vehicles field. And now use the display component to display every single vehicle. I delete all the default HTML. I start by creating a H2. I will leave the H1 for the input form. A for loop. let vehicle of vehicles. And inject one vehicle. Let's go to the materials website to pick a card to display the vehicle info. Go to the components and card. I will choose the first one. I copy the one below. And copy the Imports. It's a standalone component. And I must include the Imports. I create the input with the default constructor. I remove these buttons. I don't need them. I print the brand, the model, the year and the color. I must import the standalone component in the modules. And the HTTP client. Let's test this now. I can see the network tab that the vehicle's endpoint returns an error. The CORS error. The CORS error appears when I try to communicate a backend with a frontend which has a different URL. This occurs always with a frontend like React or Angular. What I must do is configure my backend to accept requests from my frontend. I will put it in a class named WebConfig. Add the configuration annotation and declare my Bean, with MvcConfigurer. I will only implement the CORS method. Registry at the mapping to all routes. The methods GET, POST, PUT, PATCH and DELETE. The origins localhost. Let's test this now. Now it works. Good. Let's continue with the Create endpoint. This time I will need a form to upload the vehicle information. Let's start by creating a new component. NG generate component vehicle input. I will start by creating the form and use angular materials to pick some input components. form vehicleForm, this is the name of the variable. It's an ngForm component. And I bind the submit action. This allows me to link each individual input in the form to an object in the TS file. The NG component is a ViewChild with the name vehicleForm as I put in the HTML. This is the object where angular will save the values of all the inputs of the form. I'll use an Output EventEmitter. Because after submitting the form, I want to append the created vehicle to the display list in the main component. Let's add the imports. Inject the HTTP client in the constructor. And now the onSumbit method. I call the backend with post. And I read the values from my form. And in the subscribe, I emit the response of the backend. Let's create now the H1. And use the input form with the output event which calls a method appendData. And I just push the new vehicle in the list. Let's continue now with the input form. inputs Let's pick the basic inputs. And copy the HTML. I also copied the imports from materials. I use the standalone component and import the materials components. As before, the standalone components must be declared in the modules. Let's start the server to dynamically edit the form. The brand input. The model input. The year inputs. Let's put them in vertical. I fixed a width. min width for the form. And in the main, I add some padding. Center in the middle. Okay, let's continue with my last input, the color. And now the submit button. Just pick a basic one. Copy the imports. And I add them to the existing list. Okay fine. submit And the type of the button. Now I must link each input to the model. Brand ngModel. Model, year and color. Let's test now to create a new vehicle. Here it is. Now I have the read endpoint and the create endpoint. Let's continue with the delete endpoint. This time is a DELETE method with the Id. It returns a ResponseEntity, the deleted vehicle. PathVariable And the response from the service. Just fetch the existing vehicle. And delete by Id. And return the Dto of the deleted vehicle. That's all in the backend. Let's turn to the frontend. I will now add a button to the display card to delete a single element. Let's go back to the cards. I remember some buttons in the card I copied. I copy. And past. I only need one button. Delete. And link the click event to an output event. removeItem. With just the Id of the vehicle. Let's create this output, removeItem. And bind it in the main component, removeItem. Let's create this method. I first call the backend with the delete method. And in the subscribe, I overwrite the vehicles list by filtering the deleted one. Let's test this now. Good. This is almost done. There is just one action left, the Update. I will only implement the PUT endpoint to replace all the values of the vehicle entity. Now PutMapping. ResponseEntity returns a vehicle. now I have both the PathVariable and the Body. As always, I return response from the service. I first get the existing vehicle in my database. And I will use Mapstruct to update all the fields. The target and the source. I must add MappingTarget annotation. Save the vehicle. And return it. Let's go now to the frontend. I will wrap my vehicle display component into another component. The wrapper will also contain another form to submit the changes of the vehicle. And I will switch from the display to the form when I click to the edit button. I first create the wrapper. And now the edit. I don't want to use the same component as the vehicle creation. Because I want to control the inputs individually with another method. I will use the wrapper instead of display. And now copy the display inside the wrapper. I will have both the edit and the display inside the wrapper. But it's not a for loop. It's an if. If it's editable I show the edit. If not I show the display. I will add another output event to handle the click. I will add the same output in the edit. Let's start by adding the input and output events in the wrapper. The vehicle. The removeItem. The editable field. How to handle the edit click. And how to handle the save edition. I must import the edit component because I use it inside wrapper. And the display component. Don't forget the standalone in the edit component too. And I will add the common module. Let's add another button for the edition, with another EventEmitter. editItem editItem I must import those components in the module. Let's continue now with the new login form. I first copy my input form in the edit form. Copy the methods. All the content. And the Imports. Even the CSS. Now instead of the ngForm, I will use the vehicle input object. I will use the input vehicle. And remove my previous form. Now it's not the values of the form but the input vehicle. And in the HTML, instead of using the ngForm, I will bind each input element to a field in the vehicle object. I delete the vehicle form and now it's a double binding, input output of the NG model. vehicle brand, model, year, color. Let's now test this edit form. It switched to the edit. Okay, my vehicle is updated and if I refresh the page, I download the edited vehicle. Let's make a quick recap of what I've done. I've created the backend project with Spring Initializer. I've used the web dependency, JPA, Postgres, Mapstruct, Lombok and the validation. I've created the get all vehicles endpoint, the get one vehicle, the create vehicle, the delete vehicle, and the update or replace vehicle. I've added the CORS configuration to allow a frontend with a different URL to access my backend. I've added an aspect to catch all the exceptions and return a readable message on the frontend side. I've created an Angular project with only the materials dependency. I've created a display component with a card to display a single vehicle. I've created an input component to submit a new vehicle to the backend. When the app component is initialized, I fetch all the vehicles from the backend and display them with a for loop. I've added an edit button and a delete button to the display component to edit or remove a single vehicle. And finally I've used a wrapper to switch from the display component to an edit form when I want to edit the vehicle information. That's all for this video. I could have created more endpoints to PATCH a vehicle, to search for a vehicle, and filters. Ask in the comments if you want another video about more advanced REST endpoints or API operations. Before leaving, please just click on the like button, subscribe to my channel, and see you soon. Bye!
Info
Channel: The Dev World - by Sergio Lema
Views: 4,614
Rating: undefined out of 5
Keywords: java, angular, spring boot and angular crud application, spring boot, How to Build a RESTful CRUD App With Spring Boot and Angular
Id: WTAjAo4v9qM
Channel Id: undefined
Length: 44min 26sec (2666 seconds)
Published: Mon Sep 25 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.