1. RESTful APIs (Representational State Transfer)
Characteristics:
- Stateless architecture (each request is independent).
- Uses resource-oriented design (
/users
,/products
). - Supports standard HTTP methods:
GET
,POST
,PUT
,DELETE
. - Works over HTTP(S) and supports caching.
- Preferred data format: JSON (sometimes XML).
Supporting Languages:
- Java (Spring Boot, Jersey)
- Python (Flask, Django REST Framework)
- JavaScript (Node.js with Express)
- Go (Gin, Echo)
- Ruby (Ruby on Rails)
Use Cases:
✔ Fetching user details in a mobile app (e.g., Twitter API).
✔ Managing cloud resources in AWS, Azure.
Design Guidelines:
✅ Use Nouns for Resource Naming.
Avoid like /getUsers, /createOrder. Keep URLs Short & Readable
- /users → Get all users
- /users/{id} → Get a specific user
- /orders/{orderId}/items → Get items in an order
✅ Use Proper HTTP Methods
GET /users
→ Fetch users.POST /users
→ Create a user.PUT /users/123
→ Update user with ID 123.- PATCH /users/123 → Partially (only specific fields) update user with ID 123.
DELETE /users/123
→ Delete user with ID 123.
✅ Use Query Parameters for Filtering, Sorting & Pagination- Good: /products?category=electronics&sort=price_desc&page=1&limit=10
- Bad: /getProductsByCategory/electronics/sortByPriceDescending
- Use URL versioning (
/api/v1/users
) - Alternatively, use header-based versioning (
Accept: application/vnd.api+json; version=1.0
)
200 OK
→ Success (GET, PUT, PATCH, DELETE).201 Created
→ Resource successfully created (POST)- 204 No Content → Success, no response body (DELETE)
- 400 Bad Request → Invalid request parameters
- 401 Unauthorized → Authentication failed
- 403 Forbidden → No permission to access
- 404 Not Found → Resource doesn't exist
- 409 Conflict → Data conflict (e.g., duplicate entry)
- 500 Internal Server Error → Server issue
- Good JSON Response (Structured and Consistent)
{"status": "success","data": {"id": 123,"name": "John Doe","email": "john@example.com"}}
- Bad JSON Response (Unstructured)
{"123": "John Doe","email": "john@example.com"}
- Good Error Response
{"status": "error","message": "Invalid email format","code": 400}
- Bad Error Response
{
- Use Authentication & Authorization:
- OAuth 2.0, JWT (JSON Web Tokens), API Keys
- Use RBAC (Role-Based Access Control)
- Use HTTPS:
- Always enforce HTTPS to encrypt communication.
- Rate Limiting & Throttling:
- Prevent abuse using rate limits (e.g., 100 requests per minute per user).
- Validate and Sanitize Input:
- Prevent SQL Injection, XSS, CSRF attacks.
- Use Secure Headers (CORS, CSP, etc.)
- Set Content Security Policy (CSP) and CORS headers properly.
- Use ETag headers to avoid unnecessary re-fetching
- Implement Redis or CDN caching for high-traffic APIs
- Use structured logs (JSON format) for debugging.
- Implement API monitoring tools (e.g., AWS CloudWatch, Datadog, Prometheus).
- GET
/employees/1
"name": "Alice","role": "Developer","_links": {"self": {"href": "http://localhost:8080/employees/1"},"all-employees": {"href": "http://localhost:8080/employees"}}}
- Gzip Compression
- Gzip reduces the size of HTTP responses, minimizing bandwidth usage and improving response times.
- Enable Gzip compression in a Spring Boot application
- server.compression.enabled=true
- server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
- server.compression.min-response-size=1024
- Connection Pooling
- Instead of creating a new connection for every request, a connection pool manages and reuses existing connections, reducing overhead.
- In Java, you can use HikariCP (for databases) or Apache HttpClient with connection pooling for HTTP calls.
- Example of connection pooling with Apache HttpClient (You can also use Spring WebClient for creating connection pooling)
- PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
- cm.setMaxTotal(100); // Set max connections
- cm.setDefaultMaxPerRoute(10); // Set max per route
- CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
- Example HikariCP with an MSSQL (Microsoft SQL Server) database in a Spring Boot application is straightforward. HikariCP is the default connection pool in Spring Boot, but you can configure it explicitly for fine-tuned performance.
- Add maven dependencies
<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId><version>12.2.0.jre11</version></dependency>
<dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.1.0</version></dependency>
- Spring Boot automatically detects HikariCP when present in the classpath, so you can configure it in
application.properties (You can configure HikariCP Programmatically as well)
# MSSQL Database Configurationspring.datasource.url=jdbc:sqlserver://your-server-name:1433;databaseName=your_database;encrypt=true;trustServerCertificate=truespring.datasource.username=your_usernamespring.datasource.password=your_passwordspring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver# HikariCP Configurationspring.datasource.hikari.maximum-pool-size=10 # Max connections in poolspring.datasource.hikari.minimum-idle=5 # Minimum idle connectionsspring.datasource.hikari.idle-timeout=30000 # 30 seconds idle timeoutspring.datasource.hikari.connection-timeout=30000 # 30 seconds to wait for connectionspring.datasource.hikari.max-lifetime=1800000 # 30 minutes max connection lifetime
- Verify Connection Pooling
- Use it with JdbcTemplate
- Performance Tuning Tips
- Increase
maximum-pool-size
if you expect high traffic. - Reduce
connection-timeout
to avoid slow connections. - Enable MSSQL connection pooling with
encrypt=true;trustServerCertificate=true
for security. - Set
validationQuery
to check if connections are still valid before using them.
✅ Handle Concurrency Issues → Use ETags, optimistic locking, or pessimistic locking.
Handling concurrency issues effectively ensures data consistency and prevents conflicts in multi-user or distributed systems. Here’s how you can use ETags, optimistic locking, or pessimistic locking:
1. ETags (Entity Tags) – HTTP Caching & Conditional Requests
- Used in REST APIs to prevent lost updates.
- The server generates an ETag (usually a hash of the resource).
- Clients include the ETag in requests (
If-Match
header). - If the resource is modified by another client, the update is rejected.
Best for: Web APIs, caching, and detecting resource changes.
2. Optimistic Locking – Version-based Conflict Detection
- Each record has a version number or timestamp.
- When updating, the system checks if the version matches.
- If another process modified the record, the update is rejected or retried.
Best for: High-read, low-write scenarios (e.g., financial transactions).
Example: Optimistic Locking in a Spring Boot application using JPA and@Version
annotationStep1: Entity Class with Optimistic Locking
@Data@Entity@Idpublic class Product {private Long id;@GeneratedValue(strategy = GenerationType.IDENTITY)
private String name;private double price;@Version // Enables Optimistic Locking}private int version;
@Version
ensures that when a record is updated, the version is checked.- If another transaction modified the record, a
OptimisticLockingFailureException
is thrown.
public interface ProductRepository extends JpaRepository<Product, Long> { }
Step3: Service Layer with Update Handling
@Service
private final ProductRepository productRepository;public class ProductService {
public ProductService(ProductRepository productRepository) {}this.productRepository = productRepository;
@TransactionalProduct product = productRepository.findById(productId)public void updateProductPrice(Long productId, double newPrice) {product.setPrice(newPrice);.orElseThrow(() -> new RuntimeException("Product not found"));}productRepository.save(product); // Spring handles version checking}
Step4: Handling Concurrency Exception in Controller
@RestController
@RequestMapping("/products")public class ProductController {
private final ProductService productService;public ProductController(ProductService productService) {this.productService = productService;}@PutMapping("/{id}/price")public String updatePrice(@PathVariable Long id, @RequestParam double price) {try {productService.updateProductPrice(id, price);return "Product price updated successfully!";} catch (OptimisticLockingFailureException e) {return "Update failed due to concurrent modification. Please retry.";}
}}
How It Works
- Multiple users fetch the same product (e.g.,
GET /products/1
). - Both try updating price concurrently.
- The first update succeeds, incrementing the
version
field. - The second update fails because the
version
has changed. - The API responds:
"Update failed due to concurrent modification. Please retry."
3. Pessimistic Locking – Exclusive Access Control
- The system locks the record when a process starts updating.
- Other processes must wait until the lock is released.
- Implemented using database locks (
SELECT ... FOR UPDATE
).
@Lock(LockModeType.PESSIMISTIC_WRITE)
.Step1 Entity Class
@Lock(LockModeType.PESSIMISTIC_WRITE)
: Ensures that no other transaction can modify or read the record until the current transaction is complete.
- Locks the record when fetching it, preventing other transactions from reading or updating it until this one completes.
- The
Thread.sleep(5000);
simulates a delay, making it easier to see how other transactions are blocked.
How It Works
- User A calls
PUT /products/1/price?price=150.0
, locking the record for 5 seconds. - User B tries to update the same product within those 5 seconds but gets blocked until User A’s transaction completes.
- Once User A’s transaction commits, User B’s request is processed.
When to Use Pessimistic Locking?
- High contention scenarios (e.g., inventory management, banking transactions).
- Critical data updates where lost updates cannot be tolerated.
- Not ideal for high-read applications due to performance overhead.
Idempotency ensures that repeated PUT
and DELETE
requests do not cause unintended side effects.
PUT
should update or create a resource if it doesn’t exist.DELETE
should be repeatable, meaning multiple DELETE calls should not fail.
-
PUT Idempotency:
- Use the resource ID for updates.
- If the resource exists, update it.
- If it doesn’t exist, create it (Upsert).
-
DELETE Idempotency:
- Deleting a resource should not fail if it is already deleted.
✅ Use Background Processing for Long Tasks → Don’t block API for time-consuming operations.
Long-running tasks should not block the API response. Instead, you can handle them in the background using asynchronous processing.
Use
@Async
for Background Processing: Spring provides the@Async
annotation to execute methods asynchronously.Step 1: Enable Async Processing@Configuration@EnableAsyncpublic class AsyncConfig { }Step 2: Create an Async Servicepublic class BackgroundTaskService {// This method runs asynchronouslypublic void longRunningTask() {try { Thread.sleep(5000); // Simulating a long taskSystem.out.println("Task Completed!");} catch (InterruptedException e) {Thread.currentThread().interrupt(); }}}Step 3: Call Async Methodpublic class AsyncController {private final BackgroundTaskService taskService;public AsyncController(BackgroundTaskService taskService) {this.taskService = taskService;}public String triggerTask() {taskService.longRunningTask(); // Runs in background return "Task started!";}}
✅ Provide API Documentation → Use Swagger/OpenAPI, Postman Collections for clarity and usability.
2. SOAP APIs (Simple Object Access Protocol)
Characteristics:
- XML-based strict messaging format.
- Works over HTTP, SMTP, TCP.
- Supports WS-Security (encryption, authentication).
- Uses WSDL (Web Services Description Language) for defining operations.
- High reliability and transaction support.
Supporting Languages:
- Java (JAX-WS)
- C# (.NET WCF)
- Python (Zeep)
- PHP (SOAP extension)
Use Cases:
✔ Secure banking transactions (fund transfers, credit score checks).
✔ Healthcare system integrations (hospital & pharmacy data exchange).
Design Guidelines:
✅ Use Strong XML Schema (XSD) for defining strict data formats.
✅ Follow WSDL Structure (Defines available operations and endpoints).
✅ Use WS-Security for Encryption & Authentication.
✅ Ensure Message Integrity (Use XML Signature for verification).
✅ Use MTOM (Message Transmission Optimization Mechanism) for handling large files.
✅ Implement SOAP Fault Messages for Errors (Instead of HTTP status codes).
3. GraphQL APIs
Characteristics:
- Clients request only the required data (reduces over-fetching).
- Uses a single endpoint (
/graphql
). - Supports real-time updates via subscriptions.
- Strongly typed schema with flexible queries.
- High performance for data-intensive applications.
Supporting Languages:
- JavaScript (Apollo Server, GraphQL.js)
- Python (Graphene)
- Java (GraphQL Java)
- Go (gqlgen)
- Ruby (GraphQL Ruby)
Use Cases:
✔ Fetching specific product details in an e-commerce app (e.g., Shopify API).
✔ Optimized social media feed retrieval (e.g., Facebook API).
Design Guidelines:
✅ Use a Strongly Typed Schema (Define queries, mutations, subscriptions).
✅ Support Query Batching & Caching (Optimize performance).
✅ Limit Query Depth to Prevent Overloading (Avoid deep nested queries).
✅ Use Pagination for Large Data Fetches.
✅ Implement Authorization per Field or Object Level.
✅ Use Proper Naming Conventions (CamelCase for fields, PascalCase for types).
4. gRPC (Google Remote Procedure Call)
Characteristics:
- Uses Protocol Buffers (protobuf) for fast, compact data serialization.
- Supports bidirectional streaming over HTTP/2.
- Low latency and high performance.
- Ideal for microservices communication.
- Encrypted communication using TLS.
Supporting Languages:
- Java (gRPC-Java)
- Go (gRPC-Go)
- Python (gRPC-Python)
- C++ (gRPC-C++)
- JavaScript (gRPC-Web)
Use Cases:
✔ Fast inter-service communication in Netflix, Uber.
✔ IoT devices communicating with cloud platforms.
Design Guidelines:
✅ Define Clear Service Contracts in Protobuf (.proto
files).
✅ Use Unary, Client Streaming, Server Streaming, and Bidirectional Streaming as Needed.
✅ Enable TLS Encryption for Security.
✅ Optimize gRPC Message Size for Performance.
✅ Use Load Balancing in gRPC for Scaling Services.
✅ Handle gRPC Errors Properly (Status Codes & Exception Handling
).
5. WebSockets APIs
Characteristics:
- Real-time, bidirectional communication over a persistent connection.
- Works over single TCP connection (low latency).
- Used for live data streaming (chat apps, stock market, multiplayer gaming).
- Supports text (JSON) and binary messages.
Supporting Languages:
- JavaScript (Socket.io, WebSockets API)
- Python (WebSockets, FastAPI)
- Java (Spring WebSocket)
- Golang (Gorilla WebSocket)
- C# (.NET SignalR)
Use Cases:
✔ Real-time chat applications (e.g., WhatsApp, Slack).
✔ Live stock market updates (e.g., Binance, TradingView).
Design Guidelines:
✅ Establish Secure WebSocket Connections (Use WSS for Encryption).
✅ Handle Connection Lifecycle Properly (Open, Close, Reconnect).
✅ Use Message Broadcasting for Group Communications.
✅ Implement Heartbeats/Ping Mechanism for Connection Health.
✅ Rate Limit Messages to Prevent Flooding.
✅ Graceful Disconnection Handling.
Comparison Table of API Architectures
Conclusion
Each API architecture has specific use cases:
- Use REST for general-purpose web/mobile applications.
- Use SOAP for highly secure enterprise applications.
- Use GraphQL for optimized data fetching.
- Use gRPC for high-performance, low-latency microservices.
- Use WebSockets for real-time applications like chat and live stock updates.