This REST API application provides CRUD operations for managing department data. It integrates Redis as a caching layer and PostgreSQL as the primary storage. The caching mechanism ensures optimized data retrieval, reducing database load and improving performance. Additionally, the API implements rate limiting using Redis to control excessive requests.
The API employs Redis to enhance performance through caching. The caching mechanism works as follows:
- When a request for department data is received, the system first checks Redis for the data using a specific key.
- If the data is found in Redis, it is returned directly to the client.
- If the data is not available in Redis, it is retrieved from PostgreSQL, stored in Redis for future use, and then returned to the client.
- Cached data in Redis has a predefined expiration time to ensure data consistency.
To prevent excessive requests and ensure fair API usage, the API implements a rate-limiting mechanism:
- Each request to the department controller increments a counter stored in Redis under the key
ratelimit:<key>
. - If the request count exceeds the configured limit within a specified time frame, the API returns HTTP
429 (Too Many Requests)
. - The
ratelimit:<key>
entry has a set expiration time. Once it expires, a new counter is created, resetting the request limit.
The technology used in this project are:
Spring Boot Starter Web
โ Building RESTful APIs or web applicationsRedis
โ An in-memory data store used for caching and rate limiting to enhance application performance and scalability.PostgreSQL
โ Serves as the database for storing Netflix ShowsHibernate
โ Simplifying database interactionsLombok
โ Reducing boilerplate code
The project is organized into the following package structure:
redis-cache/
โโโ src/main/java/com/yoanesber/rate_limit_with_redis/
โ โโโ ๐config/ # Configuration classes for Redis.
โ โโโ ๐controller/ # Contains REST controllers that handle HTTP requests and return responses.
โ โโโ ๐dto/ # Data Transfer Objects (DTOs) for request/response payloads.
โ โโโ ๐entity/ # Contains JPA entity classes representing database tables.
โ โโโ ๐repository/ # Provides database access functionality using Spring Data JPA.
โ โโโ ๐service/ # Business logic layer
โ โ โโโ ๐impl/ # Implementation of services
Configuration values are stored in .env.development
and referenced in application.properties
.
Example .env.development
file content:
# Application properties
APP_PORT=8081
SPRING_PROFILES_ACTIVE=development
# Database properties
SPRING_DATASOURCE_PORT=5432
SPRING_DATASOURCE_USERNAME=your_username
SPRING_DATASOURCE_PASSWORD=your_password
SPRING_DATASOURCE_DB=your_db
SPRING_DATASOURCE_SCHEMA=your_schema
# Redis properties
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_USERNAME=default
REDIS_PASSWORD=your_password
REDIS_TIMEOUT=5
REDIS_CONNECT_TIMEOUT=3
REDIS_LETTUCE_SHUTDOWN_TIMEOUT=10
Example application.properties
file content:
# Application properties
spring.application.name=rate-limit-with-redis
server.port=${APP_PORT}
spring.profiles.active=${SPRING_PROFILES_ACTIVE}
# Database properties
spring.datasource.url=jdbc:postgresql://localhost:${SPRING_DATASOURCE_PORT}/${SPRING_DATASOURCE_DB}?currentSchema=${SPRING_DATASOURCE_SCHEMA}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
# Redis properties
spring.data.redis.host=${REDIS_HOST}
spring.data.redis.port=${REDIS_PORT}
spring.data.redis.username=${REDIS_USERNAME}
spring.data.redis.password=${REDIS_PASSWORD}
spring.data.redis.timeout=${REDIS_TIMEOUT}
spring.data.redis.connect-timeout=${REDIS_CONNECT_TIMEOUT}
spring.data.redis.lettuce.shutdown-timeout=${REDIS_LETTUCE_SHUTDOWN_TIMEOUT}
The following is the database schema for the PostgreSQL database used in this project:
CREATE SCHEMA your_schema;
-- create table department
CREATE TABLE IF NOT EXISTS your_schema.department (
id character varying(4) NOT NULL,
dept_name character varying(40) NOT NULL,
active boolean NOT NULL,
created_by bigint NOT NULL,
created_date timestamp(6) without time zone NOT NULL,
updated_by bigint NOT NULL,
updated_date timestamp(6) without time zone NOT NULL,
CONSTRAINT dept_pkey PRIMARY KEY (id)
);
-- create unique constraint for dept_name
CREATE UNIQUE INDEX idx_16979_dept_name ON your_schema.department USING btree (dept_name);
-- feed data department
INSERT INTO your_schema.department (id, dept_name, active, created_by, created_date, updated_by, updated_date) VALUES
('d001', 'Marketing', true, 1, '2024-10-07 17:51:24.616', 1, '2024-11-11 16:58:30.929'),
('d002', 'Finance', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d003', 'Human Resources', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d004', 'Production', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d005', 'Development', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d006', 'Quality Management', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d007', 'Sales', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d008', 'Research', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d009', 'Customer Service', true, 1, '2024-10-07 17:51:24.616', 1, '2024-10-07 17:51:24.616'),
('d010', 'Information Technology', true, 1, '2024-10-07 17:51:24.000', 1, '2024-10-07 17:51:24.000');
A step by step series of examples that tell you how to get a development env running.
Ensure that the following dependencies are installed on your system:
- Java 17 or later
- Maven
- PostgreSQL
- Redis
- Start PostgreSQL and run the provided DDL script to set up the database schema
- Configure the connection in
.env.development
file:
# Database properties
SPRING_DATASOURCE_PORT=5432
SPRING_DATASOURCE_USERNAME=your_username
SPRING_DATASOURCE_PASSWORD=your_password
SPRING_DATASOURCE_DB=your_db
SPRING_DATASOURCE_SCHEMA=your_schema
- Ensure Redis is installed:
redis-server
- Ensure Redis is running:
redis-cli ping
Expected output:PONG
- Configure the connection in
.env.development
file:
#redis
REDIS_HOST=<host>
REDIS_PORT=<port>
REDIS_USERNAME=<username>
REDIS_PASSWORD=<password>
- Clone the repository
git clone https://github.com/yoanesber/Spring-Boot-Rate-Limit-Redis.git
cd Spring-Boot-Rate-Limit-Redis
- Run the application locally
Make sure PostgreSQL is running, then execute:
mvn spring-boot:run
- The API will be available at:
http://localhost:8081/
The API provides the following endpoints to manage department data. Each endpoint follows RESTful conventions and operates on the /departments resource. The base URL for all endpoints is http://localhost:8081
.
POST
http://localhost:8081/api/v1/departments
Request Body:
{
"id": "d011",
"deptName": "Operation",
"active": true,
"createdBy": 1001,
"createdDate": "2025-03-20T10:00:00",
"updatedBy": 1001,
"updatedDate": "2025-03-20T10:00:00"
}
Successful Response:
{
"statusCode": 201,
"timestamp": "2025-03-20T08:42:40.309979200Z",
"message": "Department saved successfully",
"data": {
"id": "d011",
"deptName": "Operation",
"active": true,
"createdBy": 1001,
"createdDate": "2025-03-20T10:00:00",
"updatedBy": 1001,
"updatedDate": "2025-03-20T10:00:00"
}
}
Duplicate Id Response:
{
"statusCode": 500,
"timestamp": "2025-03-20T08:43:00.079933Z",
"message": "An error occurred while saving department",
"data": "Department with id d011 already exists"
}
GET
http://localhost:8081/api/v1/departments
GET
http://localhost:8081/api/v1/departments/{id}
Successful Response:
{
"statusCode": 200,
"timestamp": "2025-03-20T08:47:10.730217400Z",
"message": "Department retrieved successfully",
"data": {
"id": "d011",
"deptName": "Operation",
"active": true,
"createdBy": 1001,
"createdDate": "2025-03-20T10:00:00",
"updatedBy": 1001,
"updatedDate": "2025-03-20T10:00:00"
}
}
PUT
http://localhost:8081/api/v1/departments/{id}
Request Body:
{
"deptName": "Legal",
"active": false,
"updatedBy": 1002,
"updatedDate": "2025-03-21T10:00:00"
}
Successful Response:
{
"statusCode": 200,
"timestamp": "2025-03-20T08:50:28.621609100Z",
"message": "Department updated successfully",
"data": {
"id": "d011",
"deptName": "Legal",
"active": false,
"createdBy": 1001,
"createdDate": "2025-03-20T10:00:00",
"updatedBy": 1002,
"updatedDate": "2025-03-21T10:00:00"
}
}
DELETE
http://localhost:8081/api/v1/departments/{id}
Successful Response:
{
"statusCode": 200,
"timestamp": "2025-03-20T08:52:28.884160800Z",
"message": "Department deleted successfully",
"data": null
}
If the number of requests exceeds the allowed limit within a given time frame, the API returns:
{
"statusCode": 429,
"timestamp": "2025-03-20T08:59:32.729626500Z",
"message": "Too many requests",
"data": null
}
- For the Redis Subscriber implementation, check out Spring Boot Redis Subscriber with Lettuce.
- For the Redis Publisher implementation, check out Spring Boot Redis Publisher with Lettuce.
- Rate Limit with Kong GitHub Repository, check out Spring Boot Department API with Kong API Gateway & Rate Limiting.
- Rate Limit with Bucket4j GitHub Repository, check out Rate Limiting with Bucket4j and Hazelcast.