Showing posts with label REST. Show all posts
Showing posts with label REST. Show all posts

Wednesday, May 28, 2025

REST Resource naming convention

 n REST API design, the naming of resources (URLs) should follow consistent and intuitive conventions. When dealing with pluralization, the general best practice is to use plural nouns for resource names, as they represent collections of entities.

Should you use training or trainings in the URL?

  • Use /trainings (plural) when referring to a collection of training resources.

    • ✅ Correct: GET /trainings (retrieve all trainings)

    • ✅ Correct: POST /trainings (create a new training)

    • ✅ Correct: GET /trainings/{id} (retrieve a specific training)

  • Avoid /training (singular) for collections, as it suggests a single resource rather than a collection.

    • ❌ Incorrect (for collections): GET /training

    • ❌ Incorrect (for collections): POST /training

Why Plural (/trainings)?

  1. Consistency with REST conventions – Most REST APIs use plural nouns (e.g., /users/products).

  2. Clarity – GET /trainings clearly indicates you're fetching a list, whereas GET /training could imply a singleton resource.

  3. Predictability – Easier for developers to understand and follow standard patterns.

When to Use Singular (/training)?

Only if you're referring to a singleton resource (a single, global entity). For example:

  • GET /training/config (a single configuration resource for training)

  • PUT /training/settings (global training settings)

But in most cases, /trainings is the correct choice for typical CRUD operations on multiple training records.

Final Recommendation

✅ Use /trainings for standard REST endpoints dealing with multiple training resources.
✅ Use /training/{id} (singular) only when referring to a single training instance.

Example RESTful structure:

Copy
Download
GET    /trainings        → List all trainings  
POST   /trainings        → Create a new training  
GET    /trainings/{id}   → Get a specific training  
PUT    /trainings/{id}   → Update a training  
DELETE /trainings/{id}   → Delete a training  

This follows REST best practices and keeps your API intuitive and consistent.

Wednesday, June 14, 2023

MS - Springboot + Hibernate + Rest web service chapter 1

 Create a simple REST web service using springboot.

Use spring initializer   https://start.spring.io/

Dependencies

Web, JPA, database ( Mysql Connctor ) 

for your wish you can use, lombok, actuator, devtools

Webservice will include simple CRUD operation alias with REST POST,GET, PUT, GET

Tech Stack

Java 8, Maven, Springboot 3.10 , 


Sample code as below

POM file

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.demo.micro</groupId>
<artifactId>student</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>student</name>
<description>Student Service</description>
<properties>
<java.version>17</java.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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>


Application properties

server.port=8203
spring.datasource.url=jdbc:mysql://localhost:3306/student_mgt
spring.datasource.username=root
spring.datasource.password=root

# for Spring Boot 2
# spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
# for Spring Boot 3
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=false
spring.jpa.generate-ddl=true
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl



Code base Structure

com.demo.micro.student

controller --> will include REST API

model --> model classes

repository --> repo file, db connection

service --> service classes

MainApllication --> StudentApplication with @SpringBootApplication


Controller

package com.demo.micro.student.controller;

import com.demo.micro.student.model.Student;
import com.demo.micro.student.service.StudentServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/students")
public class StudentController {

@Autowired
StudentServiceImpl studentService;

@GetMapping("")
public ResponseEntity<List<Student>> list() {
return new ResponseEntity<List<Student>>(studentService.list(), HttpStatus.OK);
}

@PostMapping("")
public ResponseEntity<Student> resgiterStudent(@RequestBody Student student) {
try {
Student st = studentService.createStudent(student);
return new ResponseEntity<>(st, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@PutMapping("")
public ResponseEntity<Student> updateStudent(@RequestBody Student student) {
return new ResponseEntity<>(studentService.createStudent(student), HttpStatus.OK);
}

@PutMapping("/{id}")
public ResponseEntity<Student> updateStudent(@PathVariable("id") int id, @RequestBody Student student) {
Optional<Student> st = studentService.findById(id);
if (st.isPresent()) {
// set details for student
return new ResponseEntity<>(studentService.createStudent(student), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

/**
* Don't do this. We normally makes a soft delete -
* 1. update flag/property as deleted. But flag is redundant field
* 2. use date/time option. nullable date time or default old time.
*
* @param student
* @return
*/
@DeleteMapping("")
public ResponseEntity<Student> deleteStudent(@RequestBody Student student) {
studentService.deleteStudent(student);
return new ResponseEntity<>(null, HttpStatus.ACCEPTED);
}
}


Model

package com.demo.micro.student.model;

import jakarta.persistence.*;
import lombok.Data;

/**
* Student details.
*/
@Entity
@Table(name = "student")
@Data
public class Student {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "student_id")
private int studentId;

/**
* First name of the student.
*/
@Column(name = "first_name")
private String firstName;

/**
* Last name of the student.
*/
@Column(name = "last_name")
private String lastName;


}

Repository

package com.demo.micro.student.repository;

import com.demo.micro.student.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudentRepository extends JpaRepository<Student,Integer> {
}


Service classes - Interface and Impl

package com.demo.micro.student.service;

import com.demo.micro.student.model.Student;

import java.util.List;
import java.util.Optional;

/**
*
*/
public interface StudentService {

/**
* Returns a list of {@link Student}s.
* @return list of {@link Student}
*/
List<Student> list();
Student createStudent(Student student);

Optional<Student> findById(int studentId);

void deleteStudent(Student student);
}


package com.demo.micro.student.service;

import com.demo.micro.student.model.Student;
import com.demo.micro.student.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class StudentServiceImpl implements StudentService {

@Autowired
StudentRepository studentRepository;

/**
* List all the {@link Student}
*
* @return
*/
@Override
public List<Student> list() {
return studentRepository.findAll();
}

/**
* Create a new record of {@link Student}.
*
* @param student
* @return
*/
@Override
public Student createStudent(Student student) {
return studentRepository.save(student);
}

@Override
public Optional<Student> findById(int studentId) {
return studentRepository.findById(studentId);
}

public void deleteStudent(Student student) {
studentRepository.delete(student);
}

}


Important : Note that we put @Srvivie at Impl class, not Interface


Main class

package com.demo.micro.student;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StudentApplication {

public static void main(String[] args) {
SpringApplication.run(StudentApplication.class, args);
}

}






Thursday, May 16, 2019

Set JSON response to servlet



<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${javax.servlet.version}</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>${gson.version}</version>
</dependency>


=======================================================
public class Employee {
     
    private int id;
     
    private String name;
     
    private String department;
    
    private long salary;
 
    // constructors
    // standard getters and setters.
}

=================================================================
String employeeJsonString = new Gson().toJson(employee);


========= setting for response ==================================
PrintWriter out = response.getWriter();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
out.print(employeeJsonString);
out.flush();


============================================================


If you use Jackson , then code will be as follows

private void writeResponse(HttpServletResponse aResponse, String errorCode, String errorMessage) {
  try {
   aResponse.setHeader("SAP-Exception", errorMessage);
   aResponse.setHeader("SAP-NFA-Exception-Code", errorCode);
   aResponse.setHeader("Content-Type", "application/json");
   ObjectMapper objectMapper = new ObjectMapper();
   NFAErrorResponse errorResponse = new NFAErrorResponse();
   errorResponse.setCode(errorCode);
   errorResponse.setMessage(errorMessage);
   String errorJsonString =objectMapper.writeValueAsString(errorResponse);
   PrintWriter out = aResponse.getWriter();
   out.print(errorJsonString);
   out.flush();
  } catch (IOException e1) {
   // TODO Auto-generated catch block
   e1.printStackTrace();
  }
 }


for further info
https://www.baeldung.com/servlet-json-response