WS API REST - fidransky/kiv-pia-labs GitHub Wiki
TOTD:
- learn about web service (WS) APIs in general
- create API specs in OpenAPI
- implement RESTful API using contract-first approach
Web service APIs in general are application APIs callable from other applications (also client) using web facilities. Various protocols may be used on the web: HTTP and SSE, WebSocket, RSocket, gRPC etc. Additionally, different data formats may be used within a protocol: JSON, XML, YAML - you name it. It's all WS APIs.
As with any API, what makes a good WS API is not only its implementation but (just as importantly) its documentation. Without an up-to-date, exhaustive and precise documentation, clients are forced to try what works and fail repeatedly, rather than implement an optimal solution on the first(-ish) try.
In contract-first approach, solution architects design the API first and test it in the wild, without implementing a single line of code. Only then, programmers grab the API specs and start implementing the API - possibly the server- and client-side at the same time. Ideally, the API specs are used to generate some baseline code, making the code generation a part of the development process. That way, the API is guaranteed to remain in sync with the documentation.
In code-first approach, the API is implemented first and later, the API documentation is extracted from the code. Until the code is finished and annotated, API documentation is non-existent, meaning that parallel development of WS API server and clients is not possible. While suboptimal, this approach may still be used in cases where API is already implemented - you just sprinkle the code with annotations and generate the docs.
REST API evolved from the HTTP protocol and it led to some enhancements to the protocol itself, mainly introduction of new methods and status codes.
The important thing to remember about REST is that:
REST is an Architecture, Not a Standard
It means you can come accross many different (often wrong) ways of implementing it while still being called REST API. Nevertheless, there are a few guidelines and best practices that should make your APIs readable, understandable and maintainable.
Please, read this short summary on REST API to understand the basics, namely:
- HTTP methods and their usage
- HTTP status codes and their meaning
- Resource naming aka URI assembly
Additionally, take a look at Richardson Maturity Model where steps toward REST API are described using four hierarchy levels.

REST API does not force use of any specific data format but you're most likely going to see it paired with JSON.
The important thing to remember is that the actual format is also a part of the API contract - server and its clients must agree on at least one data format to be able to work together. This negotiation is usually performed using Accept and Content-Type HTTP headers.
Historically, there have been many tools whose goal it is to assist developers in the API creation, documentation, implementation and testing:
- JSON Schema
- API Blueprint (by Apiary)
- RAML
- Swagger
We are covering OpenAPI (evolved from Swagger) here. While not without a fair share of flaws, it proved to be the API specification wars winner. Nowadays, there's a multitude of tools and integrations based on OpenAPI. To pick a few:
- OpenAPI Generator - Generator of client and server source code, documentation and more.
- Swagger Editor - Online OpenAPI specs editor with syntax highlighting and linting. Not necessarily useful since OpenAPI specs are just a simple YAML file, easily editable in any text editor.
- Swagger UI - API visualisation and testing tool running in browser.
- Redoc - API documentation generator.
- Prism - Mock server and contract testing tool.
- Bruno - Desktop app for using WS APIs with a possibility to import OpenAPI specs. Alternatives: Postman, Insomnia
Warning
This section is not fully updated for the 2025/2026 course.
Use online Swagger Editor (or any text editor of your choice) to create OpenAPI specs of your app's REST API. To start, read description of Semester Project to decide which model objects, API endpoints etc. are needed. Check OpenAPI Specification to learn how to create OpenAPI specs.
Create a new src/main/resources/static/api.yaml OpenAPI specs file.
Document your existing endpoints in the OpenAPI specs file. In my case, I have implemented the following endpoints in HelloWorldController:
- 
GET /hello?from={from}wherefromis a query parameter, returning HTML markup
- 
GET /numberreturning random number as plain text
OpenAPI generator provides an extensively configurable Maven plugin which can be used to generate both server- and client-side source code baseline. It comes with many different generators, one of which is spring server generator.
Add OpenAPI annotations to the dependencies section of your pom.xml file:
<dependency>
	<groupId>io.swagger.core.v3</groupId>
	<artifactId>swagger-annotations-jakarta</artifactId>
	<version>2.2.38</version>
</dependency>Add Jakarta Validation API annotations to the dependencies section of your pom.xml file:
<dependency>
	<groupId>jakarta.validation</groupId>
	<artifactId>jakarta.validation-api</artifactId>
	<!-- NOTE: we don't have to define version here, Spring Boot defines the compatible version for us
	<version>3.0.2</version>
	-->
</dependency>Next, add OpenAPI generator plugin to the build > plugins section, copying the configuration from Github:
<plugin>
	<groupId>org.openapitools</groupId>
	<artifactId>openapi-generator-maven-plugin</artifactId>
	<version>7.16.0</version>
	<executions>
		<execution>
			<phase>generate-sources</phase>
			<goals>
				<goal>generate</goal>
			</goals>
			<configuration>
				<!-- copy the configuration from GitHub -->
			</configuration>
		</execution>
	</executions>
</plugin>With the OpenAPI generator Maven plugin configured, let's use it to generate some source code. The plugin is configured to execute in generate-sources phase of Maven lifecycle. Run mvn compile to execute it.
Then, open target/generated-sources/openapi/src/main/java/ folder to see what it generated.
Finally, mark the target/generated-sources/openapi/src/main/java/ folder as Generated Sources Root (in IntelliJ IDEA, right click on the folder and select Mark Directory as).
The plugin is configured to only generate interfaces (note the interfaceOnly setting), leaving their implementation up to the developer (i.e. you).
Implement the following interfaces as Spring @RestControllers using core services (never directly repositories!) from cz.zcu.kiv.pia.labs.service package.
Add a new DamageController class implementing DamagesApi interface, annotated with @RestController in cz.zcu.kiv.pia.labs.controller package:
package cz.zcu.kiv.pia.labs.controller;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DamageController implements DamagesApi {
}Use IDE to bootstrap methods of the interface - for example, by pressing Alt+Shift+Enter.
As an example, let's implement the retrieveDamage endpoint now. Use dependency injection to autowire DamageService to the controller. Then, call DamageService#retrieveReportedDamage method to retrieve damage reports:
var result = damageService.retrieveReportedDamage();
return ResponseEntity.ok(result);As expected, this doesn't work: the DamageService#retrieveReportedDamage method returns a collection of cz.zcu.kiv.pia.labs.domain.Damage while we need to return the generated model class. We need to map Damage to DamageDTO.
There's many ways how to map one object to another: libraries such as Dozer or MapStruct serve exactly this purpose. Here, to keep things simple, we're going to use Spring's built-in ConversionService.
To map custom objects, Spring's ConversionService uses converters. Implement one such converter mapping Damage to DamageDTO:
package cz.zcu.kiv.pia.labs.converter.rest;
import cz.zcu.kiv.pia.labs.domain.Damage;
import cz.zcu.kiv.pia.labs.model.DamageDTO;
import org.springframework.core.convert.converter.Converter;
public class DamageConverter implements Converter<Damage, DamageDTO> {
	@Override
	public DamageDTO convert(Damage source) {
		return new DamageDTO()
				.id(source.getId())
				.description(source.getDescription())
				// other mappings here
				;
	}
}Next, configure ConversionService to use the newly created converter (implemented using Spring MVC, implement WebFluxConfigurer if you're using Spring WebFlux):
package cz.zcu.kiv.pia.labs.converter.converter;
import cz.zcu.kiv.pia.labs.converter.rest.DamageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.reactive.config.WebMvcConfigurer;
@Configuration
public class RestConverterConfiguration implements WebMvcConfigurer {
	@Override
	public void addFormatters(FormatterRegistry registry) {
		registry.addConverter(new DamageConverter());
	}
}Finally, autowire ConversionService to the controller and use it to map the collection of Damages to DamageDTOs:
var result = damageService.retrieveReportedDamage().stream()
		.map(damage -> conversionService.convert(damage, DamageDTO.class))
		.toList();
return ResponseEntity.ok(result);