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!