Java Rest API Structure
Project structure
The outline structure of the project is as below.
├── java
│ └── com
│ └── amido
│ └── stacks
│ └── workloads
│ └── menu
│ ├── api
│ │ ├── v1
│ │ │ ├── dto
│ │ │ │ ├── request
│ │ │ │ └── response
│ │ │ └── impl
│ │ └── v2
│ │ └── impl
│ ├── domain
│ └── mappers
└── resources
└── local
Overview of com.amido.stacks.workloads.menu
packages
Package menu.api.v1
contains the Controller
definitions, both as interface definitions where the
Swagger (OpenAPI) details are defined, and as concrete implementations under the impl
package.
Package menu.api.v2
contains the Controller
definitions for a simple V2 version of, both as interface definitions where the
Swagger (OpenAPI) details defined, and as concrete implementations under the impl
package.
Package menu.api.v1.dto
contains request and response object definitions for the different endpoints.
Package menu.api
additionally contains the OpenApiConfiguration
class, which is responsible for
defining what is displayed by the Swagger UI page for the application.
Package menu.commands
contains the OperationCode
class which holds codes for each operation, e.g. CREATE_MENU
, UPDATE_CATEGORY
etc.
Package menu.domain
contains the data object defintions for the Menu model: Menu
, Category
and Item
.
Package menu.events
contains the Event
definitions for specific API update events.
Package menu.exception
contains the custom Exception
definitions for the application.
Package menu.repository
contains the MenuRepository
interface definition, which defines repository operations and
extends CosmosRepository
, itself an extension of the Spring PagingAndSortingRepository
.
Package menu.service
contains the interface MenuQueryService
and its implementation, CosmosMenuQueryService
which relies
on the CosmosRepository to fulfil the service's actions.
Configuration
The resources
directory contains an application.yaml
file, and this is responsible for the project configuration.
This information includes Azure Cosmos DB and Application Insights configuration, which are passed as a environment variables during the deployment phase.
Below is snippet of the application.yml
configuring the Swagger, Azure and the Spring Boot specifications:
spring:
application:
name: stacks-api
data:
rest:
detection-strategy: annotated
server:
# Note: ONLY use this if you're behind a trusted Reverse Proxy, such as Application Gateway.
# If you host this app directly then users can easily inject headers.
forward-headers-strategy: framework
#######
port: 9000
management:
endpoints:
web:
base-path: /
springdoc:
swagger-ui:
disable-swagger-default-url: true
display-operation-id: true
path: /swagger/index.html
packagesToScan: com.amido.stacks.menu.api
api-docs:
groups:
enabled: true
enabled: true
path: /swagger/oas-json
azure:
cosmos:
uri: https://localhost:8081
database: Stacks
key: xxxxxx
application-insights:
instrumentation-key: xxxxxx
enabled: true
keyvault:
enabled: false
uri: xxxxx
client-id: xxxxxx
client-key: xxxxxx
tenant-id: xxxxxx
servicebus:
connectionString: xxxxx
topicName: sbt-menu-events
subscriptionName: sbs-menu-events
enabled: true
Note: Keys with the xxxxx
value are placeholders meant to be replaced with values from your environment, as necessary.
Application logging
Java provides a simple logging abstraction with most of required logging features used by an application. Whenever logging is required in the application, the class will instantiate an instance of Logger from slf4j to use as the logger object. The Logger instances are created by Logging Factory registered by each application and will abstract the logging library from the application logging.
In the application, we have customized how the logs are created. There is a logback-spring.xml
file in the resources
directory
to add the customized features, as below:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="console">
<encoder>
<pattern>%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) | %X{CorrelationId} |
%highlight(%-5level) %logger{36}.%M - %msg%n
</pattern>
</encoder>
</appender>
<appender class="com.microsoft.applicationinsights.logback.ApplicationInsightsAppender"
name="aiAppender">
</appender>
<root level="debug">
<appender-ref ref="console"/>
</root>
<root level="info">
<appender-ref ref="aiAppender"/>
</root>
</configuration>
Application Insights
Azure Application Insights is the chosen logging platform, and will aggregate all logs generated by all services.
Integrating with Application Insights ensures all logs generated (and filtered) are forwarded to the logging platform
for correlation and potential future investigation. Below is the section that needs to be added the application.yml
to
enable it to interact with Application Insights:
application-insights:
instrumentation-key: xxxxxx
enabled: true
Additionally, an AI-Agent.xml
definition is included in the resource
directory to enable deeper data insights:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsightsAgent>
<Instrumentation>
<BuiltIn>
<Logging threshold="info"/>
</BuiltIn>
</Instrumentation>
</ApplicationInsightsAgent>
Any events published by the application will have a correlation id, to enable tracking of requests, responses and any exceptions.
Authorization with Auth0
The API endpoints are protected by an OAuth 2.0 provider.
The OAuth 2.0 client is configured as an Auth0 instance. All requests must include a Bearer
token in the Authorization
header.
There is an auth.properties
file which configures the authorization definitions required to use
the application with in conjunction with Auth0 to secure access to endpoints with JWT. If this property is set:
auth.isEnabled=true
then a valid JWT is required to be sent with the header in the request to the API endpoint.
Other Auth0 properties defined in this file, and used by spring security for validating the token are:
auth0.issuer
- the issuer of the JWT Token. Typically, this is your Auth0 domain prefixed byhttps://
auth0.apiAudience
- the unique identifier for your API,https://.../api/v2/
Testing Auth0 JWT security using Swagger UI
-
Open the swagger UI page at http://localhost:9000/swagger/index.html once the application is up and running locally.
-
Send a POST request via the
Auth
endpoint on the Swagger UI page for your configured API definition, containing the following payload:{
"client_id": "REDACTED",
"client_secret": "REDACTED",
"audience": "https://REDACTED/api/v2/",
"grant_type": "client_credentials"
} -
You should receive a response containing a valid token something like
{
"access_token": "eyJhbGciOiJSU...wd6WXw",
"expires_in": 86400,
"token_type": "Bearer"
} -
Click on the
Authorise
button on the Swagger UI page and paste the token. -
Endpoints should now work only with a valid token.