💬 LLM Streaming Chat
Real-time token streaming with Claude API via Server-Sent Events. Messages stream token-by-token for low perceived latency.
claude-chat-demo.app
Hello! I am Claude. Ask me anything about AI, testing, or software engineering.
Python — Streaming with Claude API
import anthropic
client = anthropic.Anthropic()
# FastAPI SSE streaming endpoint
from fastapi import FastAPI
from sse_starlette.sse import EventSourceResponse
import json
app = FastAPI()
@app.post("/chat/stream")
async def chat(prompt: str, system: str = "You are a helpful AI."):
async def generate():
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
system=system,
messages=[{"role": "user", "content": prompt}]
) as stream:
for text in stream.text_stream:
yield f"data: {json.dumps({'text': text})}\n\n"
yield "data: [DONE]\n\n"
return EventSourceResponse(generate())
# Frontend (React)
# import { useChat } from 'ai/react'
# const { messages, input, handleSubmit } = useChat({
# api: '/api/chat/stream'
# })
📝 Text Summarization
Summarise long documents with configurable style using LangChain + Claude. Supports executive summaries, bullet points, and one-sentence abstracts.
summarizer.app / summarize
Python — LangChain Summarization Chain
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0, max_tokens=512)
prompt = ChatPromptTemplate.from_template(
"Summarise the following text as a {style}.\n"
"Be concise and accurate.\n\nText: {text}\n\nSummary:"
)
chain = prompt | llm | StrOutputParser()
def summarise(text: str, style: str = "executive summary") -> str:
return chain.invoke({"text": text, "style": style})
# Long-doc map-reduce for documents exceeding context
from langchain.chains.summarize import load_summarize_chain
from langchain_text_splitters import RecursiveCharacterTextSplitter
def summarise_long(text: str) -> str:
docs = RecursiveCharacterTextSplitter(
chunk_size=4000, chunk_overlap=400
).create_documents([text])
return load_summarize_chain(llm, chain_type="map_reduce").invoke(docs)
❤ Sentiment Analysis
Multi-dimensional sentiment analysis using structured LLM output. Returns sentiment, confidence, emotions, and key phrases as validated JSON.
sentiment-api.app / analyze
Python — Structured Sentiment Output
import anthropic, json
from pydantic import BaseModel
client = anthropic.Anthropic()
class SentimentResult(BaseModel):
sentiment: str # positive/negative/neutral/mixed
confidence: float # 0.0 to 1.0
emotions: dict[str, float]
key_phrases: list[str]
reasoning: str
def analyse_sentiment(text: str) -> SentimentResult:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512, temperature=0,
system="""Analyse sentiment. Respond with JSON only:
{
"sentiment": "positive|negative|neutral|mixed",
"confidence": 0.0-1.0,
"emotions": {"joy": 0.8, "frustration": 0.3},
"key_phrases": ["fast API", "confusing docs"],
"reasoning": "..."
}""",
messages=[{"role": "user", "content": text}]
)
return SentimentResult(**json.loads(resp.content[0].text))
from fastapi import FastAPI
app = FastAPI()
@app.post("/analyse", response_model=SentimentResult)
def analyse(text: str):
return analyse_sentiment(text)
💻 AI Code Generation
Generate production-ready code from natural language descriptions. Select language, optionally include tests, and get idiomatic code instantly.
ai-codegen.app / generate
Python — AI Code Generator Service
import anthropic, json
client = anthropic.Anthropic()
SYSTEM = """You are an expert software engineer.
Generate clean, production-ready code with type hints,
docstrings, and error handling.
Include pytest tests if requested.
Respond with JSON: {
"code": "...",
"language": "python",
"explanation": "...",
"tests": "..."
}"""
def generate_code(
description: str,
language: str = "python",
include_tests: bool = True
) -> dict:
prompt = (
f"Generate {language} code for: {description}\n"
+ ("Include comprehensive tests." if include_tests else "")
)
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048, temperature=0,
system=SYSTEM,
messages=[{"role": "user", "content": prompt}]
)
return json.loads(resp.content[0].text)
result = generate_code("Email validator", "python", True)
print(result["code"])
print(result["tests"])
🔍 RAG Semantic Search
Retrieval-Augmented Generation — semantic search over a knowledge base with LLM-generated answers grounded in retrieved context chunks.
rag-search.app / query
Python — RAG with LangChain + ChromaDB
from langchain_anthropic import ChatAnthropic, AnthropicEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
embeddings = AnthropicEmbeddings()
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
prompt = ChatPromptTemplate.from_template(
"Answer using ONLY the context below.\n"
"If not in context, say 'I don't know.'\n\n"
"Context:\n{context}\n\nQuestion: {question}\n\nAnswer:"
)
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0)
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt | llm | StrOutputParser()
)
# RAGAS evaluation
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy
results = evaluate(test_dataset,
metrics=[faithfulness, answer_relevancy])
print(results) # faithfulness: 0.95, answer_relevancy: 0.91
⚙ AI Agent Reasoning Loop
Watch a ReAct agent reason through a task step-by-step — selecting tools, executing actions, and processing observations until the goal is complete.
agent.app / run
Python — LangGraph ReAct Agent
from langgraph.prebuilt import create_react_agent
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
import subprocess
@tool
def run_tests(test_path: str = ".") -> str:
"Run pytest and return results."
result = subprocess.run(
["pytest", test_path, "-v", "--tb=short"],
capture_output=True, text=True, timeout=60
)
return result.stdout[-3000:]
@tool
def read_file(path: str) -> str:
"Read a source file."
with open(path) as f:
return f.read()
@tool
def search_errors(pattern: str) -> str:
"Search for error patterns in test files."
result = subprocess.run(
["grep", "-r", pattern, "./tests"],
capture_output=True, text=True
)
return result.stdout
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0)
agent = create_react_agent(llm, [run_tests, read_file, search_errors])
# Stream agent steps
for event in agent.stream({
"messages": [{"role": "user",
"content": "Find failing tests and summarise errors"}]
}, stream_mode="values"):
msg = event["messages"][-1]
print(f"[{msg.type.upper()}] {str(msg.content)[:150]}")
🔐 User Authentication
JWT access tokens, refresh tokens, bcrypt password hashing, OAuth2 flow, and role-based access control.
localhost:8080/auth/login
// Spring Security — generate JWT on login
@PostMapping("/auth/login")
public ResponseEntity<AuthResponse> login(
@RequestBody LoginRequest req) {
var auth = authManager.authenticate(
new UsernamePasswordAuthenticationToken(
req.email(), req.password()));
var user = (UserDetails) auth.getPrincipal();
var token = jwtService.generateToken(user);
return ResponseEntity.ok(
new AuthResponse(token, "Bearer", 900));
}
// JWT Service
public String generateToken(UserDetails user) {
return Jwts.builder()
.subject(user.getUsername())
.claim("role", extractRole(user))
.issuedAt(new Date())
.expiration(new Date(
System.currentTimeMillis() + 900_000))
.signWith(secretKey)
.compact();
}
// Express + jsonwebtoken
router.post('/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const accessToken = jwt.sign(
{ sub: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ sub: user._id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
res.cookie('refreshToken', refreshToken, {
httpOnly: true, secure: true, sameSite: 'strict'
});
res.json({ accessToken, tokenType: 'Bearer', expiresIn: 900 });
});
from passlib.context import CryptContext
from jose import jwt
from datetime import datetime, timedelta, timezone
pwd_ctx = CryptContext(schemes=["bcrypt"])
@router.post("/auth/token")
async def login(
form: OAuth2PasswordRequestForm = Depends(),
db: AsyncSession = Depends(get_db),
):
user = await db.scalar(
select(User).where(User.email == form.username))
if not user or not pwd_ctx.verify(form.password, user.password):
raise HTTPException(401, "Invalid credentials")
token = jwt.encode({
"sub": str(user.id),
"role": user.role,
"exp": datetime.now(timezone.utc) + timedelta(minutes=15),
}, settings.jwt_secret, algorithm="HS256")
return {"access_token": token, "token_type": "bearer"}
API Endpoints
POST/auth/registerCreate account201
POST/auth/loginLogin → JWT + refresh cookie200
POST/auth/refreshRotate refresh token200
POST/auth/logoutBlacklist token + clear cookie204
GET/auth/meGet current user200
📋 CRUD Operations
REST API design with full Create, Read, Update, Delete — pagination, filtering, sorting, and validation.
localhost:8080/api/v1/users
Users — 3 of 42 results
| Name | Role | Status | |
|---|---|---|---|
KJ Kavita J | kavita@lab.com | Admin | Active |
AS Alex S | alex@lab.com | User | Active |
RK Riya K | riya@lab.com | Moderator | Pending |
Page 1 of 3
// Spring Boot — paginated CRUD
@GetMapping
public Page<UserDto> getUsers(
@RequestParam(defaultValue="0") int page,
@RequestParam(defaultValue="20") int size,
@RequestParam(defaultValue="name") String sort,
@RequestParam(required=false) String search) {
var pageable = PageRequest.of(page, size, Sort.by(sort));
return search == null
? userRepo.findAll(pageable).map(mapper::toDto)
: userRepo.searchByName(search, pageable).map(mapper::toDto);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserDto createUser(@Valid @RequestBody CreateUserRequest req) {
if (userRepo.existsByEmail(req.email()))
throw new ConflictException("Email already exists");
return mapper.toDto(userRepo.save(mapper.toEntity(req)));
}
@PutMapping("/{id}")
public UserDto updateUser(@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest req) {
var user = userRepo.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(id));
mapper.updateEntity(user, req);
return mapper.toDto(userRepo.save(user));
}
// Express — paginated CRUD
router.get('/', authenticate, async (req, res, next) => {
try {
const { page = 1, limit = 20, search, sort = 'name' } = req.query;
const skip = (page - 1) * limit;
const filter = search
? { name: { $regex: search, $options: 'i' } } : {};
const [users, total] = await Promise.all([
User.find(filter).sort(sort).skip(skip).limit(+limit).lean(),
User.countDocuments(filter),
]);
res.json({
data: users, page: +page, limit: +limit,
total, pages: Math.ceil(total / limit)
});
} catch (err) { next(err); }
});
router.post('/', authenticate, validate(createSchema), async (req, res, next) => {
try {
const exists = await User.findOne({ email: req.body.email });
if (exists) return res.status(409).json({ error: 'Email exists' });
const user = await User.create(req.body);
res.status(201).json(user);
} catch (err) { next(err); }
});
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select, func
router = APIRouter(prefix="/api/v1/users", tags=["users"])
@router.get("/", response_model=PaginatedResponse[UserResponse])
async def list_users(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
search: str | None = None,
sort: str = "name",
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = select(User)
if search:
q = q.where(User.name.ilike(f"%{search}%"))
total = await db.scalar(select(func.count()).select_from(q.subquery()))
q = q.order_by(getattr(User, sort)).offset((page-1)*size).limit(size)
users = (await db.execute(q)).scalars().all()
return {"data": users, "page": page, "size": size, "total": total}
@router.post("/", response_model=UserResponse, status_code=201)
async def create_user(body: UserCreate, db: AsyncSession = Depends(get_db)):
if await db.scalar(select(User).where(User.email == body.email)):
raise HTTPException(409, "Email already exists")
user = User(**body.model_dump())
db.add(user)
await db.commit()
await db.refresh(user)
return user
REST Endpoint Map
GET/api/v1/users?page=1&limit=20&search=kav&sort=name200
POST/api/v1/usersCreate user (validated body)201
GET/api/v1/users/:idGet single user200
PUT/api/v1/users/:idFull update200
DELETE/api/v1/users/:idSoft delete204
💬 Real-Time Chat
WebSocket connections with rooms, presence detection, typing indicators, and Redis Pub/Sub for horizontal scaling.
ws://localhost:3000/ws/room-general
# general
3 online
Hey team! Ready to ship? 🚀
Almost! Just fixing the auth bug
Need help? I can pair on it
All good! Tests passing now ✅
// Spring WebSocket Config
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry reg) {
reg.enableSimpleBroker("/topic", "/queue");
reg.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry reg) {
reg.addEndpoint("/ws").withSockJS();
}
}
// Message Controller
@Controller
public class ChatController {
@MessageMapping("/chat/{roomId}/send")
@SendTo("/topic/chat/{roomId}")
public ChatMessage send(@DestinationVariable String roomId,
@Payload ChatMessage msg,
Principal principal) {
msg.setSender(principal.getName());
msg.setTimestamp(Instant.now());
chatService.persist(msg);
return msg;
}
}
// Socket.io server
io.on('connection', (socket) => {
socket.on('join-room', async (roomId) => {
socket.join(roomId);
const history = await Message.find({ roomId })
.sort({ createdAt: -1 }).limit(50);
socket.emit('message-history', history.reverse());
socket.to(roomId).emit('user-joined', {
userId: socket.user.id, timestamp: new Date()
});
});
socket.on('send-message', async ({ roomId, text }) => {
const msg = await Message.create({
roomId, text, sender: socket.user.id
});
io.to(roomId).emit('new-message', msg);
});
socket.on('typing', ({ roomId }) =>
socket.to(roomId).emit('user-typing', { userId: socket.user.id })
);
});
from fastapi import WebSocket, WebSocketDisconnect
class ConnectionManager:
def __init__(self):
self.rooms: dict[str, list[WebSocket]] = {}
async def connect(self, ws: WebSocket, room: str):
await ws.accept()
self.rooms.setdefault(room, []).append(ws)
async def broadcast(self, room: str, msg: dict, exclude=None):
for ws in self.rooms.get(room, []):
if ws != exclude:
await ws.send_json(msg)
manager = ConnectionManager()
@app.websocket("/ws/{room_id}")
async def chat_endpoint(ws: WebSocket, room_id: str, token: str):
user = verify_token(token)
await manager.connect(ws, room_id)
try:
while True:
data = await ws.receive_json()
msg = {"text": data["text"], "userId": user.id}
await manager.broadcast(room_id, msg, exclude=ws)
except WebSocketDisconnect:
manager.rooms[room_id].remove(ws)
📂 File Upload & Management
Multipart file uploads, S3/MinIO storage, progress tracking, thumbnail generation, and signed URL downloads.
localhost:3000/files
📤 Drop files here or click to upload
PNG, JPG, PDF, ZIP — max 50MB
project-spec-v2.pdf
2.4 MB · PDF
hero-banner.png
840 KB · PNG
// Spring Boot S3 upload
@PostMapping("/upload")
public ResponseEntity<FileResponse> upload(
@RequestParam MultipartFile file) {
if (file.getSize() > 50 * 1024 * 1024)
throw new BadRequestException("File too large");
String key = "uploads/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
s3Client.putObject(PutObjectRequest.builder()
.bucket(bucket).key(key)
.contentType(file.getContentType()).build(),
RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
String url = s3Client.utilities()
.getUrl(b -> b.bucket(bucket).key(key)).toString();
return ResponseEntity.ok(new FileResponse(key, url, file.getSize()));
}
// Generate presigned download URL
public String presign(String key) {
return s3Presigner.presignGetObject(b -> b
.getObjectRequest(r -> r.bucket(bucket).key(key))
.signatureDuration(Duration.ofMinutes(15))).url().toString();
}
// Multer + S3 upload
import multer from 'multer';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 50_000_000 } });
const s3 = new S3Client({ region: process.env.AWS_REGION });
router.post('/upload', authenticate, upload.single('file'), async (req, res) => {
const key = `uploads/${crypto.randomUUID()}-${req.file.originalname}`;
await s3.send(new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: key,
Body: req.file.buffer,
ContentType: req.file.mimetype,
}));
const url = await getSignedUrl(s3,
new GetObjectCommand({ Bucket: process.env.S3_BUCKET, Key: key }),
{ expiresIn: 900 }
);
res.json({ key, url, size: req.file.size });
});
import boto3, uuid
from fastapi import UploadFile, File, Depends
s3 = boto3.client("s3", region_name=settings.aws_region)
@router.post("/upload", response_model=FileResponse)
async def upload_file(
file: UploadFile = File(...),
current_user: User = Depends(get_current_user),
):
if file.size and file.size > 50 * 1024 * 1024:
raise HTTPException(400, "File too large")
key = f"uploads/{uuid.uuid4()}-{file.filename}"
content = await file.read()
s3.put_object(
Bucket=settings.s3_bucket,
Key=key,
Body=content,
ContentType=file.content_type,
)
url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": settings.s3_bucket, "Key": key},
ExpiresIn=900,
)
return {"key": key, "url": url, "size": len(content)}
📊 Data Visualization Dashboard
Live metrics, aggregation pipelines, Chart.js visualizations, date-range filtering, and auto-refresh.
localhost:3000/dashboard
$48,290
Revenue (30d)
↑ 12.4%
1,284
New Users
↑ 8.1%
Revenue by Category
// Spring Data JPA — revenue aggregation
@Query(value = """
SELECT category,
SUM(amount) AS revenue,
COUNT(*) AS order_count,
AVG(amount) AS avg_order
FROM orders
WHERE status = 'COMPLETED'
AND created_at >= :start
GROUP BY category
ORDER BY revenue DESC
""", nativeQuery = true)
List<RevenueByCategory> revenueByCategory(
@Param("start") LocalDateTime start);
// Dashboard DTO projection
public interface RevenueByCategory {
String getCategory();
BigDecimal getRevenue();
Long getOrderCount();
BigDecimal getAvgOrder();
}
// Controller
@GetMapping("/metrics/revenue")
@PreAuthorize("hasRole('ADMIN')")
public List<RevenueByCategory> revenue(
@RequestParam @DateTimeFormat(iso = DATE_TIME) LocalDateTime from) {
return orderRepo.revenueByCategory(from);
}
// MongoDB aggregation
const revenue = await Order.aggregate([
{ $match: { status: 'completed',
createdAt: { $gte: new Date(Date.now() - 30*864e5) } } },
{ $group: {
_id: '$category',
revenue: { $sum: '$amount' },
count: { $sum: 1 },
avg: { $avg: '$amount' }
}},
{ $sort: { revenue: -1 } },
{ $project: {
category: '$_id',
revenue: { $round: ['$revenue', 2] },
count: 1, avg: { $round: ['$avg', 2] }, _id: 0
}}
]);
// Cache for 5 minutes
const cached = await redis.get('metrics:revenue');
if (cached) return JSON.parse(cached);
await redis.setex('metrics:revenue', 300, JSON.stringify(revenue));
from sqlalchemy import select, func, case
from datetime import datetime, timedelta
@router.get("/metrics/revenue")
async def revenue_by_category(
days: int = Query(30, ge=1, le=365),
db: AsyncSession = Depends(get_db),
_: User = Depends(require_role("admin")),
):
since = datetime.utcnow() - timedelta(days=days)
result = await db.execute(
select(
Order.category,
func.sum(Order.amount).label("revenue"),
func.count().label("count"),
func.avg(Order.amount).label("avg_order"),
)
.where(Order.status == "completed", Order.created_at >= since)
.group_by(Order.category)
.order_by(func.sum(Order.amount).desc())
)
return [
{"category": r.category, "revenue": float(r.revenue),
"count": r.count, "avg": float(r.avg_order)}
for r in result
]
🛒 E-commerce Mini App
Product catalog, shopping cart, Stripe payment integration, order management, and inventory tracking.
localhost:3000/shop
Featured Products
Cart (2 items) 🛒
💻
Dev Laptop
$1,299.00
🖥️
4K Monitor
$549.00
⌨️
Mech Keyboard
$189.00
🖱️
Ergonomic Mouse
$79.00
// Stripe checkout session (Java)
@PostMapping("/checkout")
public ResponseEntity<CheckoutResponse> createCheckout(
@RequestBody CheckoutRequest req,
Principal principal) {
var user = userService.findByEmail(principal.getName());
var items = req.items().stream().map(item ->
SessionCreateParams.LineItem.builder()
.setQuantity(item.quantity())
.setPriceData(SessionCreateParams.LineItem.PriceData.builder()
.setCurrency("usd")
.setUnitAmount(item.price() * 100)
.setProductData(SessionCreateParams.LineItem.PriceData
.ProductData.builder().setName(item.name()).build())
.build())
.build()
).toList();
var session = Session.create(SessionCreateParams.builder()
.setMode(SessionCreateParams.Mode.PAYMENT)
.setSuccessUrl(frontendUrl + "/success?session_id={CHECKOUT_SESSION_ID}")
.setCancelUrl(frontendUrl + "/cart")
.addAllLineItem(items)
.putMetadata("userId", user.getId().toString())
.build());
return ResponseEntity.ok(new CheckoutResponse(session.getUrl()));
}
// Stripe checkout session (Node.js)
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
router.post('/checkout', authenticate, async (req, res) => {
const { items } = req.body;
const lineItems = items.map(item => ({
price_data: {
currency: 'usd',
unit_amount: Math.round(item.price * 100),
product_data: { name: item.name, images: [item.imageUrl] },
},
quantity: item.quantity,
}));
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: lineItems,
success_url: `${process.env.FRONTEND_URL}/success?sid={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.FRONTEND_URL}/cart`,
metadata: { userId: req.user.id },
});
res.json({ url: session.url });
});
import stripe
stripe.api_key = settings.stripe_secret_key
@router.post("/checkout")
async def create_checkout(
body: CheckoutRequest,
current_user: User = Depends(get_current_user),
):
line_items = [
{
"price_data": {
"currency": "usd",
"unit_amount": int(item.price * 100),
"product_data": {"name": item.name},
},
"quantity": item.quantity,
}
for item in body.items
]
session = stripe.checkout.Session.create(
mode="payment",
line_items=line_items,
success_url=f"{settings.frontend_url}/success?sid={{CHECKOUT_SESSION_ID}}",
cancel_url=f"{settings.frontend_url}/cart",
metadata={"user_id": str(current_user.id)},
)
return {"url": session.url}
🌐 API Integration
Third-party API clients, webhook receivers, rate limiting, caching, retry with exponential backoff, and circuit breakers.
localhost:8080/api/integrations
Connected APIs
🟢 LiveStripe Payments1,234 calls/day
🟢 LiveSendGrid Email456 emails/day
🟡 SlowOpenAI API89 calls/day
🔴 ErrorWeather APIRate limited
Recent Webhook Events
✅ payment.succeeded — order#4821
✅ email.delivered — welcome@user.com
⚠️ payment.failed — retry in 3h
✅ customer.created — id_84jK2
✅ email.delivered — welcome@user.com
⚠️ payment.failed — retry in 3h
✅ customer.created — id_84jK2
// Spring — resilient HTTP client with Resilience4j
@Service
public class WeatherService {
private final WebClient client;
@CircuitBreaker(name = "weather", fallbackMethod = "fallback")
@Retry(name = "weather")
@TimeLimiter(name = "weather")
public CompletableFuture<WeatherDto> getCurrent(String city) {
return client.get()
.uri("/current?city={city}&appid={key}", city, apiKey)
.retrieve()
.onStatus(HttpStatus::is4xxClientError,
r -> Mono.error(new WeatherClientException(city)))
.bodyToMono(WeatherDto.class)
.toFuture();
}
private CompletableFuture<WeatherDto> fallback(String city, Exception e) {
log.warn("Weather circuit open for {}", city);
return CompletableFuture.completedFuture(WeatherDto.unknown());
}
}
// Webhook handler with signature verification
@PostMapping("/webhooks/stripe")
public ResponseEntity<Void> stripeWebhook(
@RequestBody String payload,
@RequestHeader("Stripe-Signature") String sig) {
Event event = Webhook.constructEvent(payload, sig, webhookSecret);
switch (event.getType()) {
case "payment_intent.succeeded" -> handlePayment(event);
case "customer.subscription.deleted" -> cancelSubscription(event);
}
return ResponseEntity.ok().build();
}
// Node.js — retry with exponential backoff
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const resp = await fetch(url, options);
if (resp.status === 429) {
const retryAfter = +resp.headers.get('Retry-After') || 60;
await sleep(retryAfter * 1000);
continue;
}
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return await resp.json();
} catch (err) {
if (attempt === maxRetries) throw err;
await sleep(Math.pow(2, attempt) * 1000 + Math.random() * 500);
}
}
}
// Stripe webhook verification
router.post('/webhooks/stripe',
express.raw({ type: 'application/json' }),
(req, res) => {
const event = stripe.webhooks.constructEvent(
req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET
);
if (event.type === 'payment_intent.succeeded') handlePayment(event.data.object);
res.json({ received: true });
}
);
import httpx, asyncio
from tenacity import retry, stop_after_attempt, wait_exponential
# Retry with exponential backoff
@retry(stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=60))
async def call_external_api(url: str, params: dict) -> dict:
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.get(url, params=params,
headers={"Authorization": f"Bearer {settings.api_key}"})
resp.raise_for_status()
return resp.json()
# Stripe webhook
@router.post("/webhooks/stripe")
async def stripe_webhook(
request: Request,
stripe_signature: str = Header(None),
):
payload = await request.body()
try:
event = stripe.Webhook.construct_event(
payload, stripe_signature, settings.stripe_webhook_secret)
except stripe.error.SignatureVerificationError:
raise HTTPException(400, "Invalid signature")
if event["type"] == "payment_intent.succeeded":
await handle_payment(event["data"]["object"])
return {"received": True}