DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Develop a Spring Boot REST API in AWS: PART 4 (CodePipeline / CI/CD)
  • Leveraging Salesforce Using Spring Boot
  • Spring Boot REST API Request Body Validation Example Using a Custom Validator
  • Spring Microservices RESTFul API Documentation With Swagger Part 1

Trending

  • From Zero to Production: Best Practices for Scaling LLMs in the Enterprise
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • Docker Model Runner: Streamlining AI Deployment for Developers
  1. DZone
  2. Coding
  3. Frameworks
  4. Monitoring Using Spring Boot 2.0, Prometheus, and Grafana (Part 1 — REST API)

Monitoring Using Spring Boot 2.0, Prometheus, and Grafana (Part 1 — REST API)

Read this tutorial in order to learn how to create a REST API for CRUD by using Spring Boot 2.0, Prometheus, and Grafana.

By 
Satish Sharma user avatar
Satish Sharma
·
May. 18, 18 · Tutorial
Likes (9)
Comment
Save
Tweet
Share
38.8K Views

Join the DZone community and get the full member experience.

Join For Free

In part 1, we will be creating a REST API for CRUD operations using Spring Boot 2.0, JPA, H2 Database, and SWAGGER UI for documentation.

We will be creating a simple application offering CRUD operations over REST for a person entity we shall be using

  • H2: as our underlying database

  • Spring Boot Web: for creating REST API

  • Spring Data JPA: for JPA implementation

  • SWAGGER UI: for documenting API's

So, let's get started by creating a new project.

Create a spring starter project in Eclipse (I am using STS) or you can use Spring Initializer to get started. Add dependencies for Web, Lombok, Actuator, H2, and JPA.Image title


At this point, our project structure should look like this:project structure

Now, let's add controller, entity, model, and service classes.

  • pom .xml: Maven pom file containing all the dependencies

<?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.satish.monitoring</groupId>
    <artifactId>person-application</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>person-application</name>
    <description>Sample application to be monitored</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath />
        <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <springfox-version>2.5.0</springfox-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- DB-JPA boot related -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--SpringFox swagger dependencies -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox-version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox-version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
        <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>
  • PersonEntity: JPA entity class representing person table in the database.

package com.satish.monitoring.db.entities;

import java.io.Serializable;

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

import com.satish.monitoring.web.models.Person;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@Table(name = "PERSON")
@NoArgsConstructor
public class PersonEntity implements Serializable{
private static final long serialVersionUID = -8003246612943943723L;

@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private int personId;

private String firstName;
private String lastName;
private String email;

public PersonEntity( String firstName, String lastName, String email) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}

public PersonEntity(int personId, String firstName, String lastName, String email) {
super();
this.personId = personId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}
  • PersonRepository: JPA repository interface.
    package com.satish.monitoring.db.repositories;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    import com.satish.monitoring.db.entities.PersonEntity;
    
    @Repository
    public interface PersonRepository  extends JpaRepository<PersonEntity, Integer>{
    }
  • PersonService: interface for operations. 
  • package com.satish.monitoring.services;
    
    import java.util.List;
    import java.util.Optional;
    
    import org.springframework.stereotype.Service;
    
    import com.satish.monitoring.web.models.Person;
    
    @Service
    public interface PersonService {
    /**
     * 
     * @param personId
     * @return {@link Optional} {@link Person} objects if present in database
     *         for supplied person ID
     */
    public Optional<Person> getPersonById(int personId);
    
    /**
     * 
     * @return {@link List} of {@link Person} model class fo rall available
     *         entities
     */
    public List<Person> getAllPersons();
    
    /**
     * 
     * @param personId
     * @return Delete the person from database for supplied id
     */
    public boolean removePerson(int personId);
    
    /**
     * 
     * @param person
     * @return {@link Optional} {@link Person} objects after save or update Save
     *         if no personId present else update
     */
    public Optional<Person> saveUpdatePerson(Person person);
    }
    
    • PersonServiceImpl: The implementation class to interact with Database using repository interface.

    package com.satish.monitoring.services;
    
    import java.util.List;
    import java.util.Optional;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.satish.monitoring.db.entities.PersonEntity;
    import com.satish.monitoring.db.repositories.PersonRepository;
    import com.satish.monitoring.web.models.Person;
    
    @Component
    public class PersonServiceImpl implements PersonService {
    
    private final PersonRepository personRepo;
    
    @Autowired
    PersonServiceImpl(PersonRepository personRepo) {
    this.personRepo = personRepo;
    }
    
    /**
     * Convert {@link Person} Object to {@link PersonEntity} object Set the
     * personId if present else return object with id null/0
     */
    private final Function<Person, PersonEntity> personToEntity = new Function<Person, PersonEntity>() {
    @Override
    public PersonEntity apply(Person person) {
    if (person.getPersonId() == 0) {
    return new PersonEntity(person.getFirstName(), person.getLastName(), person.getEmail());
    } else {
    return new PersonEntity(person.getPersonId(), person.getFirstName(), person.getLastName(),
    person.getEmail());
    }
    }
    };
    
    /**
     * Convert {@link PersonEntity} to {@link Person} object
     */
    private final Function<PersonEntity, Person> entityToPerson = new Function<PersonEntity, Person>() {
    @Override
    public Person apply(PersonEntity entity) {
    return new Person(entity.getPersonId(), entity.getFirstName(), entity.getLastName(), entity.getEmail());
    }
    };
    
    
    /**
     * If record is present then convert the record else return the empty {@link Optional}
     */
    @Override
    public Optional<Person> getPersonById(int personId) {
    return  personRepo.findById(personId).map(s ->  entityToPerson.apply(s));
    }
    
    @Override
    public List<Person> getAllPersons() {
    return personRepo.findAll().parallelStream()
    .map(s ->  entityToPerson.apply(s))
    .collect(Collectors.toList());
    }
    
    @Override
    public boolean removePerson(int personId) {
    personRepo.deleteById(personId);
    return true;
    }
    
    @Override
    public Optional<Person> saveUpdatePerson(Person person) {
    if(person.getPersonId() == 0 || personRepo.existsById(person.getPersonId())){
    PersonEntity entity = personRepo.save(personToEntity.apply(person));
    return Optional.of(entityToPerson.apply(entity));
    }else{
    return Optional.empty();
    }
    }
    }
  • PersonResource : Controller class to expose endpoints
  • package com.satish.monitoring.web.rest;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.satish.monitoring.services.PersonService;
    import com.satish.monitoring.web.models.Person;
    
    
    @RestController
    @RequestMapping("/person")
    public class PersonResource {
    
    private final PersonService personService;
    /**
     * Constructor to autowire PersonService instance.
     *  Look we have declared personService as final without initialization
     */
    @Autowired
    PersonResource(PersonService personService) {
    this.personService = personService;
    }
    
    /**
     * 
     * @return expose GET endpoint to return {@link List} of all available persons
     */
    @GetMapping
    public List<Person> getAllPerson() {
    return personService.getAllPersons();
    }
    
    /**
     * 
     * @param personId supplied as path variable
     * @return expose GET endpoint to return  {@link Person} for the supplied person id 
     * return HTTP 404 in case person is not found in database
     */
    @GetMapping(value = "/{personId}")
    public ResponseEntity<Person> getPerson(@PathVariable("personId") int personId) {
    return personService.getPersonById(personId).map(person -> {
    return ResponseEntity.ok(person);
    }).orElseGet(() -> {
    return new ResponseEntity<Person>(HttpStatus.NOT_FOUND);
    });
    }
    
    /**
     * 
     * @param person JSON body
     * @return  expose POST mapping and return newly created person in case of successful operation
     * return HTTP 417 in case of failure
     */
    @PostMapping
    public ResponseEntity<Person> addNewPerson(@RequestBody Person person) {
    return personService.saveUpdatePerson(person).map(p -> {
    return ResponseEntity.ok(p);
    }).orElseGet(() -> {
    return new ResponseEntity<Person>(HttpStatus.EXPECTATION_FAILED);
    });
    }
    
    /**
     * 
     * @param person JSON body
     * @return  expose PUT mapping and return newly created or updated person in case of successful operation
     * return HTTP 417 in case of failure
     *  
     */
    @PutMapping
    public ResponseEntity<Person> updatePerson(@RequestBody Person person) {
    return personService.saveUpdatePerson(person).map(p -> {
    return ResponseEntity.ok(p);
    }).orElseGet(() -> {
    return new ResponseEntity<Person>(HttpStatus.EXPECTATION_FAILED);
    });
    }
    /**
     * 
     * @param personId person id to be deleted
     * @return expose DELETE mapping and return success message if operation was successful. 
     *  return HTTP 417 in case of failure 
     * 
     */
    @DeleteMapping(value = "/{personId}")
    public ResponseEntity<String> deletePerson(@PathVariable("personId") int personId) {
    if (personService.removePerson(personId)) {
    return ResponseEntity.ok("Person with id : " + personId + " removed");
    } else {
    return new ResponseEntity<String>("Error deleting enitty ", HttpStatus.EXPECTATION_FAILED);
    }
    }
    }
  • SwaggerAPIDocumentationConfig: Configure SWAGGER UI
  • package com.satish.monitoring.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    /**
     *
     * @author Satish Sharma
     *
     */
    @EnableSwagger2
    @Configuration
    public class SwaggerAPIDocumentationConfig {
    
    ApiInfo apiInfo() {
    return new ApiInfoBuilder().title("Person REST CRUD operations API in Spring-Boot 2")
    .description(
    "Sample REST API for monitoring using Spring Boot, Prometheus and Graphana ")
    .termsOfServiceUrl("").version("0.0.1-SNAPSHOT").contact(new Contact("Satish Sharma", "https://github.com/hellosatish/monitoring/person", "https://github.com/hellosatish")).build();
    }
    
    @Bean
    public Docket configureControllerPackageAndConvertors() {
    return new Docket(DocumentationType.SWAGGER_2).select()
    .apis(RequestHandlerSelectors.basePackage("com.satish.monitoring")).build()
     .directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
                    .directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
                    .apiInfo(apiInfo());
    }
    }
  • SwagerUIController: To expose SWAGGER UI from context path and redirect all request to Swagger UI.
  • package com.satish.monitoring.web.rest;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class SwaggerUIController {
    @RequestMapping(value = "/")
    public String index() {
    return "redirect:swagger-ui.html";
    }
    }
    • application.properties: Configure properties. Notice we have configured the application to run on port 9000.

    # Server configurations.
    server.port=9000
    logging.level.com.satish.monitoring=debug
    logging.file=logs/monitoring.log
    
    # Database configurations.
    spring.datasource.driver-class-name=org.h2.Driver
    spring.datasource.url=jdbc:h2:file:./db/target/person_db;DB_CLOSE_DELAY=-1
    spring.datasource.username=satish
    spring.datasource.data=classpath:/db-scripts/init-script.sql
    
    spring.h2.console.enabled=true
    spring.h2.console.path=/db-console
    
    spring.jpa.show-sql=true
    # change the below to none in production
    spring.jpa.hibernate.ddl-auto=create-drop
  • Run: We are all set to go. Now, let's run the application using the command below. Or in STS, you can right-click on the project in project explorer and select Run As and then select Spring Boot App .
  • mvn clean spring-boot:run

    Now browse the URL http:localhost:9000 and you should be able to see the SWAGGER UI 

    Image title

    You have successfully created the REST API for CRUD operations. You can have a look/download the code from this GitHub Repo.

    In the next part, we shall be enabling endpoint to expose metrics as JSON.

    Spring Framework REST API Web Protocols Spring Boot Grafana

    Opinions expressed by DZone contributors are their own.

    Related

    • Develop a Spring Boot REST API in AWS: PART 4 (CodePipeline / CI/CD)
    • Leveraging Salesforce Using Spring Boot
    • Spring Boot REST API Request Body Validation Example Using a Custom Validator
    • Spring Microservices RESTFul API Documentation With Swagger Part 1

    Partner Resources

    ×

    Comments
    Oops! Something Went Wrong

    The likes didn't load as expected. Please refresh the page and try again.

    ABOUT US

    • About DZone
    • Support and feedback
    • Community research
    • Sitemap

    ADVERTISE

    • Advertise with DZone

    CONTRIBUTE ON DZONE

    • Article Submission Guidelines
    • Become a Contributor
    • Core Program
    • Visit the Writers' Zone

    LEGAL

    • Terms of Service
    • Privacy Policy

    CONTACT US

    • 3343 Perimeter Hill Drive
    • Suite 100
    • Nashville, TN 37211
    • support@dzone.com

    Let's be friends:

    Likes
    There are no likes...yet! 👀
    Be the first to like this post!
    It looks like you're not logged in.
    Sign in to see who liked this post!