RESTful web services are built to work best on the Web. Representational State Transfer (REST) means that when a RESTful API is called, the server will transfer to the client a representation of the state of the requested resource.
In this interactive tutorial, we will build a RESTful API that will be implemented in a simple note-taking application using CRUD methods i.e: create, retrieve, update and deletion of notes.
Let's get our hands dirty..
What we will be using:
1. Spring Boot - this will be our MVC based framework based on Java.
2. MySQL - our relational database system and where our schema will run
3. Postman - for handling our API calls
4. An IDE with the Spring development environment -
Netbeans/STS suite/Eclipse can work.
Spring Boot provides a web tool called Spring Initializer to bootstrap an application quickly. Just go to http://start.spring.io and follow the steps below to generate a new project.
Step 1 : Click Switch to full version on http://start.spring.io page.
Step 2 : Enter the details as the screenshot below shows:
Once all the details are entered, click Generate Project to generate and download your project. Spring Initializer will generate the project with the details you have entered and download a zip file with all the project folders.
Next, Unzip the downloaded zip file and import it into your favorite IDE.
A few details on our newly created application -
- RhapsodyNotesApplication
This is the main entry point of our Spring Boot application.
package com.example.rhapsodynotes;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootApplication
public class RhapsodyNotesApplication {
public static void main(String[] args) {
SpringApplication.run(EasyNotesApplication.class, args);
}
}
It contains a simple annotation called @SpringBootApplication
which is a combination of the following more specific spring annotations:
@EnableAutoConfiguration: This annotation tells Spring to automatically configure your application based on the dependencies that you have added in the pom.xml file.
For example, If spring-data-jpa or spring-jdbc is in the classpath, then it automatically tries to configure a DataSource by reading the database properties from application.properties file.
The main() method calls Spring Boot’s SpringApplication.run() method to launch the application.
- resources/
This directory, as the name suggests, is dedicated to all the static resources, templates and property files.
resources/static - contains static resources such as css, js and images.
resources/templates - contains server-side templates which are rendered by Spring.
resources/application.properties - This file is very important. It contains application-wide properties. Spring reads the properties defined in this file to configure your application. You can define server’s default port, server’s context path, database URLs etc, in this file.
You can refer this file for common application properties used in Spring Boot.
- pom.xml - contains all the project dependencies
Configuring MySQL Database
As I pointed out earlier, Spring Boot tries to auto-configure a DataSource if spring-data-jpa is in the classpath by reading the database configuration from application.properties file.
Open application.properties file and add the following properties to it.
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url =
jdbc:mysql://localhost:3306/notes_app?useSSL=false
spring.datasource.username = root
spring.datasource.password = 303seminarian
## Hibernate Properties
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
You will need to create a database named notes_app in MySQL, and change the spring.datasource.username & spring.datasource.password properties as per your MySQL installation.
In the above properties file, the last two properties are for hibernate. Spring Boot uses Hibernate as the default JPA implementation.
The property spring.jpa.hibernate.ddl-auto is used for database initialization. I’ve used the value “update” for this property.
It does two things -
When you define a domain model, a table will automatically be created in the database and the fields of the domain model will be mapped to the corresponding columns in the table.
Any change to the domain model will also trigger an update to the table. For example, If you change the name or type of a field, or add another field to the model, then all these changes will be reflected in the mapped table as well.
Using update for spring.jpa.hibernate.ddl-auto property is fine for development. But, For production, You should keep the value of this property to “validate”, and use a database migration tool like Flyway for managing changes in the database schema.
Creating the Note model
Let’s now create the Note model. Our Note model has following fields -
id: Primary Key with Auto Increment.
title: The title of the Note. (NOT NULL field)
content: Note’s content. (NOT NULL field)
createdAt: Time at which the Note was created.
updatedAt: Time at which the Note was updated.
Now, let’s see how we can model this in Spring. Create a new package called model inside com.example.rhapsodynotes
and add a class named Note.java
with following contents -
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.rhaposdynotes.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.io.Serializable;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.util.Date;
/**
*
* @author Kimaiga
*/
@Entity
@Table(name = "notes")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"},
allowGetters = true)
public class Note implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String title;
@NotBlank
private String content;
@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdAt;
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private Date updatedAt;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
Enable JPA in the main application.
Open RhapsodyNotesApplication.java
and add @EnableJpaAuditing
annotation.
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.rhapsodynotes.repository;
import com.example.rhapsodynotes.model.Note;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
* @author Kimaiga
*/
public interface NoteRepository extends JpaRepository<Note, Long> {
}
Creating NoteRepository
to access data from the database
The next thing we’re going to do is create a repository to access Note’s data from the database.
Spring Data JPA has got us covered here. It comes with a JpaRepository
interface which defines methods for all the CRUD operations on the entity, and a default implementation of JpaRepository
called SimpleJpaRepository
.
Still sailing! Let’s create the repository now. First, Create a new package called repository inside the base package com.example.rhapsodynotes
. Then, create an interface called NoteRepository
and extend it from JpaRepository
:
/** To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.*/
package com.example.rhapsodynotes.repository;
import com.example.rhapsodynotes.model.Note;
import org.springframework.data.jpa.repository.JpaRepository;
/**
*
* @author Kimaiga
*/
public interface NoteRepository extends JpaRepository<Note, Long> {
}
That is all you have to do in the repository layer. You will now be able to use JpaRepository
’s methods like save()
, findOne()
, findAll()
, count()
, delete()
etc.
You don’t need to implement these methods. They are already implemented by Spring Data JPA’s SimpleJpaRepository
. This implementation is plugged in by Spring automatically at runtime.
Exception Handling
We’ll define the Rest APIs for creating, retrieving, updating, and deleting a Note in the next section.
The APIs will throw a ResourceNotFoundException
whenever a Note with a given id is not found in the database.
Following is the definition of ResourceNotFoundException
. (I’ve created a package named exception inside com.example.rhapsodyotes to store this exception class):
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.rhapsodynotes.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
*
* @author Kimaiga
*/
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private String resourceName;
private String fieldName;
private Object fieldValue;
/**
* Creates a new instance of <code>ResourceNotFoundException</code> without
* detail message.
*/
public ResourceNotFoundException( String resourceName, String fieldName, Object fieldValue) {
super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue));
this.resourceName = resourceName;
this.fieldName = fieldName;
this.fieldValue = fieldValue;
}
/**
* Constructs an instance of <code>ResourceNotFoundException</code> with the
* specified detail message.
*
* @param msg the detail message.
*/
public String getResourceName(){
return resourceName;
}
public String getFieldName() {
return fieldName;
}
public Object getFieldValue() {
return fieldValue;
}
public ResourceNotFoundException(String msg) {
super(msg);
}
}
Notice the use of @ResponseStatus
annotation in the above exception class. This will cause Spring boot to respond with the specified HTTP status code whenever this exception is thrown from your controller.
Creating our NoteController
We’ll now create the REST APIs for creating, retrieving, updating and deleting a Note.
First, create a new package controller inside com.example.rhapsodynotes. Then, create a new class NoteController.java
with the following code snippet -
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.example.easynotes.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.easynotes.exception.ResourceNotFoundException;
import com.example.rhapsodynotes.model.Note;
import com.example.rhapsody.repository.NoteRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
*
* @author Kimaiga
*/
@RestController
@RequestMapping("/api")
public class NoteController {
@Autowired
NoteRepository noteRepository;
// Get All Notes
@GetMapping("/notes")
public List<Note> getAllNotes() {
return noteRepository.findAll();
}
// Create a new Note
@PostMapping("/notes")
public Note createNote(@Valid @RequestBody Note note) {
return noteRepository.save(note);
}
// Get a Single Note
@GetMapping("/notes/{id}")
public Note getNoteById(@PathVariable(value = "id") Long noteId) {
return noteRepository.findById(noteId).orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
}
// Update a Note
@PutMapping("/notes/{id}")
public Note updateNote(@PathVariable(value = "id") Long noteId, @Valid @RequestBody Note noteDetails) {
Note note = noteRepository.findById(noteId).orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
note.setTitle(noteDetails.getTitle());
note.setContent(noteDetails.getContent());
Note updatedNote = noteRepository.save(note);
return updatedNote;
}
// Delete a Note
@DeleteMapping("/notes/{id}")
public ResponseEntity<?> deleteNote(@PathVariable(value = "id") Long noteId) {
Note note = noteRepository.findById(noteId).orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);
return ResponseEntity.ok().build();
}
}
@RestController
annotation is a combination of Spring’s @Controller
and @ResponseBody
annotations.
The @Controller
annotation is used to define a controller and the @ResponseBody
annotation is used to indicate that the return value of a method should be used as the response body of the request.
@RequestMapping("/api") declares that the url for all the apis in this controller will start with /api.
Testing the APIs
We’ve successfully built all the apis for our application. Let’s now run the app and test the apis.
The application will start at Spring Boot’s default port 8080. It’s time to test our apis using postman. Add the URL: http://localhost:8080/easy-notes/api/notes/
on your postman URL bar and select a HTTP method to make a request. Based on the API calls, you should be able to get various response codes. Remember the input type to use is in JSON format.
We've successfully built a Restful CRUD API using Spring Boot, Mysql, Jpa and Hibernate.
You can find the source code for this tutorial on my github repository. Feel free to clone the repository and build upon it.