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:


Frontend Gateway with PostgreSQL: IntelliJ Step-by-Step Guide

Objective: Build a stateless gateway with PostgreSQL caching for API responses and SSR fallbacks.


Phase 1: Project Setup in IntelliJ

Step 1.1 – Create a New Maven Project

  1. Open IntelliJ β†’ File β†’ New β†’ Project.
  2. Select:
    • Maven
    • JDK 11+
    • Archetype: org.apache.maven.archetypes:maven-archetype-quickstart
  3. Name: frontend-gateway β†’ Finish.

Phase 2: PostgreSQL Setup

Step 2.1 – Run PostgreSQL via Docker

  1. 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:

Step 1: Open Terminal in IntelliJ

  • Press Alt+F12 (Windows/Linux) or Option+F12 (Mac) to open the terminal within IntelliJ IDEA.

Step 2: Run the Docker Command

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

Explanation of the Command:

  • docker run: Creates and starts a new Docker container.
  • -d: Runs the container in detached mode (in the background).
  • -p 5432:5432: Maps port 5432 on your local machine to port 5432 in the container (PostgreSQL default port).
  • --name postgres: Names the container postgres for easy reference.
  • -e POSTGRES_USER=gateway: Sets the PostgreSQL username to gateway.
  • -e POSTGRES_PASSWORD=secret: Sets the PostgreSQL password to secret.
  • -e POSTGRES_DB=gateway_cache: Creates a database named gateway_cache on startup.
  • postgres:latest: Uses the latest official PostgreSQL image from Docker Hub.

Step 3: Verify the Container is Running

Run this command in the terminal to check if the container is up:

docker ps

You should see postgres listed with status Up.

Step 4: Connect to PostgreSQL (Optional)

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.

Troubleshooting:

  • 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

Step 2.2 – Initialize Tables

  1. Open Database Tool Window (View β†’ Tool Windows β†’ Database).
  2. Click + β†’ Data Source β†’ PostgreSQL.
    • Host: localhost
    • Port: 5432
    • User: gateway
    • Password: secret
    • Database: gateway_cache β†’ Test Connection β†’ OK.
  3. Right-click the database β†’ New β†’ Query Console:
    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);
    Click Execute (▢️).

Phase 3: Backend (Spring Boot)

Step 3.1 – Add Dependencies

  1. 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>
  2. Reload Maven: Click the Maven Tool Window (right sidebar) β†’ Reload (πŸ”„).

Step 3.2 – Configure Spring Boot

  1. 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

Step 3.3 – Implement Caching

  1. Create entity class:
    Right-click src/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;
    }
  2. 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);
    }
  3. Add scheduled cleanup:

    @SpringBootApplication
    @EnableScheduling
    public class GatewayApplication {
      public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
      }
    }
  4. 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:

  1. Right-click src/main/java β†’ New β†’ Java Class
  2. Name it ApiResponse
  3. 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).
  1. 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.
  1. Enable Scheduling (Automatic Cache Cleanup)
  • To periodically delete expired cache entries.

Steps:

  1. Create New Java Class
  2. Right-click β†’ New β†’ Java Class
  3. 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.

Step 3.4 – Run the Application

  1. Right-click GatewayApplication β†’ Run.
  2. 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.

Phase 4: Frontend (Node.js/Express)

Step 4.1 – Setup Node.js in IntelliJ

  1. File β†’ New β†’ Project β†’ Node.js.
  2. Name: client-adapters β†’ Create.

Step 4.2 – Install Dependencies

  1. Open Terminal in IntelliJ:
    npm install express pg

Step 4.3 – Implement SSR Fallback

  1. 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'));
  2. Run: Right-click server.js β†’ Run.

Step 4.4 – Test SSR Fallback

  1. Insert test data via Database Tool Window:
    INSERT INTO ssr_cache (route, html, expiry)
    VALUES ('/home', '<div>Homepage Fallback</div>', NOW() + INTERVAL '1 day');
  2. Open http://localhost:3000/fallback/home β†’ Output:
    <div>Homepage Fallback</div>

Phase 5: Debugging & Validation

Step 5.1 – Debug Java Backend

  1. Set breakpoints in ApiController.java.
  2. Right-click GatewayApplication β†’ Debug.
  3. Trigger requests via browser/Postman and inspect variables.

Step 5.2 – Monitor PostgreSQL

  1. Use Database Tool Window to:
    • View api_responses table after API calls.
    • Check ssr_cache for SSR templates.

Phase 6: Deploy from IntelliJ

Step 6.1 – Dockerize Backend

  1. Create Dockerfile in project root:
    FROM openjdk:11
    COPY target/frontend-gateway-1.0-SNAPSHOT.jar /app.jar
    ENTRYPOINT ["java", "-jar", "/app.jar"]
  2. Build and run:
    mvn package
    docker build -t gateway-backend .
    docker run -p 8080:8080 gateway-backend

Key IntelliJ Shortcuts

Action Shortcut
Run/Debug Shift+F10 / F9
Open Terminal Alt+F12
Database Tool Window View β†’ Tool Windows β†’ Database
Reload Maven Maven Tool Window β†’ Reload (πŸ”„)

Final Architecture

graph TD
  IntelliJ -->|Develop| Java[Spring Boot]
  IntelliJ -->|Develop| Node[Express.js]
  Java -->|Cache| PostgreSQL
  Node -->|Read Cache| PostgreSQL
  Java -->|Call| Microservices
Loading

Done! You now have a fully integrated Frontend Gateway with PostgreSQL caching, developed and tested in IntelliJ.

⚠️ **GitHub.com Fallback** ⚠️