1. Frontend Gateway with PostgreSQL - Wiz-DevTech/prettygirllz GitHub Wiki
Hereβs a step-by-step guide for implementing the Frontend Gateway with PostgreSQL caching using IntelliJ IDEA, optimized for clarity and hands-on development:
Objective: Build a stateless gateway with PostgreSQL caching for API responses and SSR fallbacks.
- Open IntelliJ β File β New β Project.
- Select:
- Maven
- JDK 11+
- Archetype:
org.apache.maven.archetypes:maven-archetype-quickstart
- Name:
frontend-gateway
β Finish.
- Open Terminal in IntelliJ (Alt+F12):
docker run -d -p 5432:5432 --name postgres \ -e POSTGRES_USER=gateway \ -e POSTGRES_PASSWORD=secret \ -e POSTGRES_DB=gateway_cache \ postgres:latest
for PowerShell (Windows) Modify the command to use backticks ` instead of \ for line continuation:
powershell
docker run -d -p 5432:5432 --name postgres -e POSTGRES_USER=gateway
-e POSTGRES_PASSWORD=secret -e POSTGRES_DB=gateway_cache
postgres:latest
docker run -d -p 5432:5432 --name postgres -e POSTGRES_USER=gateway -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=gateway_cache postgres:latest
Here's a step-by-step guide to run PostgreSQL via Docker in IntelliJ IDEA:
- Press
Alt+F12
(Windows/Linux) orOption+F12
(Mac) to open the terminal within IntelliJ IDEA.
Copy and paste the following command into the terminal:
docker run -d -p 5432:5432 --name postgres \
-e POSTGRES_USER=gateway \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=gateway_cache \
postgres:latest
-
docker run
: Creates and starts a new Docker container. -
-d
: Runs the container in detached mode (in the background). -
-p 5432:5432
: Maps port5432
on your local machine to port5432
in the container (PostgreSQL default port). -
--name postgres
: Names the containerpostgres
for easy reference. -
-e POSTGRES_USER=gateway
: Sets the PostgreSQL username togateway
. -
-e POSTGRES_PASSWORD=secret
: Sets the PostgreSQL password tosecret
. -
-e POSTGRES_DB=gateway_cache
: Creates a database namedgateway_cache
on startup. -
postgres:latest
: Uses the latest official PostgreSQL image from Docker Hub.
Run this command in the terminal to check if the container is up:
docker ps
You should see postgres
listed with status Up
.
If you want to verify the database, you can connect using:
docker exec -it postgres psql -U gateway -d gateway_cache
- Type
\q
to exit.
- If Docker is not installed, download it from Docker's official website.
- If you get a port conflict (
5432
already in use), either stop the existing service or change the host port (e.g.,-p 5433:5432
). - If the container fails to start, check logs with:
docker logs postgres
- Open Database Tool Window (View β Tool Windows β Database).
- Click + β Data Source β PostgreSQL.
- Host:
localhost
- Port:
5432
- User:
gateway
- Password:
secret
- Database:
gateway_cache
β Test Connection β OK.
- Host:
- Right-click the database β New β Query Console:
Click Execute (
CREATE TABLE ssr_cache ( route VARCHAR(255) PRIMARY KEY, html TEXT NOT NULL, expiry TIMESTAMP WITH TIME ZONE ); CREATE TABLE api_responses ( id VARCHAR(255) PRIMARY KEY, api_key VARCHAR(255) NOT NULL, response_json JSONB NOT NULL, expiry TIMESTAMP WITH TIME ZONE ); CREATE INDEX idx_api_responses_expiry ON api_responses (expiry);
βΆοΈ ).
-
Open
pom.xml
β Add inside<dependencies>
:<!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.0</version> </dependency> <!-- PostgreSQL --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency>
-
Reload Maven: Click the Maven Tool Window (right sidebar) β Reload (π).
- Create
src/main/resources/application.yml
:spring: datasource: url: jdbc:postgresql://localhost:5432/gateway_cache username: gateway password: secret jpa: hibernate: ddl-auto: validate properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect
-
Create entity class:
Right-clicksrc/main/java
β New β Java Class:@Entity @Table(name = "api_responses") public class ApiResponse { @Id private String id; private String apiKey; @Column(columnDefinition = "jsonb") private String responseJson; private Instant expiry; }
-
Create repository:
public interface ApiResponseRepository extends JpaRepository<ApiResponse, String> { @Modifying @Query("DELETE FROM ApiResponse a WHERE a.expiry < :now") void deleteExpired(@Param("now") Instant now); }
-
Add scheduled cleanup:
@SpringBootApplication @EnableScheduling public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
-
Create a REST controller:
@RestController @RequestMapping("/api") public class ApiController { @Autowired private ApiResponseRepository repository; @GetMapping("/products/{id}") public String getProduct(@PathVariable String id) { Optional<ApiResponse> cached = repository.findById(id); if (cached.isPresent()) { return cached.get().getResponseJson(); } else { String mockResponse = "{\"name\":\"Sample Product\"}"; // Replace with actual API call repository.save(new ApiResponse(id, "product-api", mockResponse, Instant.now().plusSeconds(3600))); return mockResponse; } } }
should look like this 1a. Create the ApiResponse Entity This entity will store cached API responses.
Steps:
- Right-click src/main/java β New β Java Class
- Name it ApiResponse
- Add the following code:
import jakarta.persistence.*;
import java.time.Instant;
@Entity
@Table(name = "api_responses")
public class ApiResponse {
@Id
private String id; // Unique identifier (e.g., API request key)
private String apiKey; // API key (for authentication)
@Column(columnDefinition = "jsonb")
private String responseJson; // Cached JSON response
private Instant expiry; // Expiry time for cache invalidation
// Constructors, Getters, and Setters
public ApiResponse() {}
public ApiResponse(String id, String apiKey, String responseJson, Instant expiry) {
this.id = id;
this.apiKey = apiKey;
this.responseJson = responseJson;
this.expiry = expiry;
}
// Getters & Setters (Generated
Key Annotations:
- @Entity β Marks this as a JPA entity.
- @Table(name = "api_responses") β Maps to the api_responses table.
- @Column(columnDefinition = "jsonb") β Stores responseJson as PostgreSQL jsonb (binary JSON).
- Create the ApiResponseRepository (JPA Repository)
- This repository handles database operations.
Steps:
- Right-click src/main/java β New β Interface
- Name it ApiResponseRepository
Step-by-Step: Creating an Interface in IntelliJ
Navigate to src/main/java
Open your project in IntelliJ.
Expand the src/main/java folder in the Project pane.
Right-Click β New
Right-click on the package where you want to create the interface (e.g., com.example.repository).
Select New β Java Class (yes, even for interfaces).
Enter Interface Name
In the dialog box, type the name of the interface (e.g., ApiResponseRepository).
Instead of selecting Class from the dropdown, choose Interface.
- Add the following code:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.time.Instant;
public interface ApiResponseRepository extends JpaRepository<ApiResponse, String> {
// Delete expired cache entries
@Modifying
@Query("DELETE FROM ApiResponse a WHERE a.expiry < :now")
void deleteExpired(@Param("now") Instant now);
}
Key Features:
- JpaRepository<ApiResponse, String> β Provides CRUD operations.
- @Modifying + @Query β Custom delete query for expired entries.
- Enable Scheduling (Automatic Cache Cleanup)
- To periodically delete expired cache entries.
Steps:
- Create New Java Class
- Right-click β New β Java Class
- Name it GatewayApplication
- Modify the main @SpringBootApplication class (GatewayApplication.java):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling // Enables scheduled tasks
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
2a.Create a scheduled task (e.g., CacheCleanupScheduler.java):
Create new file CacheCleanupScheduler.java
Right-click your package β New β Java Class
Name: CacheCleanupScheduler
Location: src/main/java/com/yourpackage/scheduler/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Instant;
@Component
public class CacheCleanupScheduler {
@Autowired
private ApiResponseRepository repository;
// Runs every hour (3600000 ms)
@Scheduled(fixedRate = 3600000)
public void cleanExpiredCache() {
repository.deleteExpired(Instant.now());
System.out.println("Cleaned expired cache entries.");
}
}
Key Annotations:
- @EnableScheduling β Enables Springβs scheduled tasks.
- @Scheduled(fixedRate = 3600000) β Runs every hour (adjust as needed).
4a. Create a REST Controller (ApiController) This handles API requests and caches responses.
Steps:
- Right-click src/main/java β New β Class
- Name it ApiController
- Add the following code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.util.Optional;
@RestController
@RequestMapping("/api")
public class ApiController {
@Autowired
private ApiResponseRepository repository;
@GetMapping("/products/{id}")
public String getProduct(@PathVariable String id) {
// Check cache first
Optional<ApiResponse> cached = repository.findById(id);
if (cached.isPresent()) {
return cached.get().getResponseJson(); // Return cached response
} else {
// Simulate API call (replace with real API call)
String mockResponse = "{\"name\":\"Sample Product\"}";
// Cache the response for 1 hour
repository.save(
new ApiResponse(
id,
"product-api",
mockResponse,
Instant.now().plusSeconds(3600)
)
);
return mockResponse;
}
}
}
Key Logic:
- Cache Hit β Returns stored JSON.
- Cache Miss β Calls API, stores response, and sets expiry.
5a. Test the Implementation Run the Spring Boot app:
mvn spring-boot:run
Test the endpoint:
- First call (GET /api/products/123) β Fetches from "API" and caches.
- Second call β Returns cached response.
Check PostgreSQL:
SELECT * FROM api_responses;
- Should show cached entries.
- Right-click
GatewayApplication
β Run. - Verify: Open
http://localhost:8080/api/products/123
in a browser.-
First call: Returns
{"name":"Sample Product"}
and caches it. - Second call: Serves from PostgreSQL cache.
-
First call: Returns
- File β New β Project β Node.js.
- Name:
client-adapters
β Create.
- Open Terminal in IntelliJ:
npm install express pg
-
Create
server.js
:const express = require('express'); const { Pool } = require('pg'); const pool = new Pool({ user: 'gateway', password: 'secret', host: 'localhost', database: 'gateway_cache' }); const app = express(); app.get('/fallback/:route', async (req, res) => { const { rows } = await pool.query( 'SELECT html FROM ssr_cache WHERE route = $1 AND expiry > NOW()', [req.params.route] ); res.send(rows[0]?.html || '<div>No cached version</div>'); }); app.listen(3000, () => console.log('Server running on port 3000'));
-
Run: Right-click
server.js
β Run.
- Insert test data via Database Tool Window:
INSERT INTO ssr_cache (route, html, expiry) VALUES ('/home', '<div>Homepage Fallback</div>', NOW() + INTERVAL '1 day');
- Open
http://localhost:3000/fallback/home
β Output:<div>Homepage Fallback</div>
- Set breakpoints in
ApiController.java
. - Right-click
GatewayApplication
β Debug. - Trigger requests via browser/Postman and inspect variables.
- Use Database Tool Window to:
- View
api_responses
table after API calls. - Check
ssr_cache
for SSR templates.
- View
- Create
Dockerfile
in project root:FROM openjdk:11 COPY target/frontend-gateway-1.0-SNAPSHOT.jar /app.jar ENTRYPOINT ["java", "-jar", "/app.jar"]
- Build and run:
mvn package docker build -t gateway-backend . docker run -p 8080:8080 gateway-backend
Action | Shortcut |
---|---|
Run/Debug | Shift+F10 / F9 |
Open Terminal | Alt+F12 |
Database Tool Window | View β Tool Windows β Database |
Reload Maven | Maven Tool Window β Reload (π) |
graph TD
IntelliJ -->|Develop| Java[Spring Boot]
IntelliJ -->|Develop| Node[Express.js]
Java -->|Cache| PostgreSQL
Node -->|Read Cache| PostgreSQL
Java -->|Call| Microservices
Done! You now have a fully integrated Frontend Gateway with PostgreSQL caching, developed and tested in IntelliJ.