AI · Full-Stack · Case study
FinGraph AI
Banking assistant over a Neo4j relationship graph. pgvector semantic search finds the relevant customers, loans and chats, Neo4j enriches every match with its relationships, and Groq writes the answer. JWT + bcrypt authentication guards the admin and audit APIs.
- Team
- Solo
- Timeline
- January 2026
- Stack
- Node.js · Express · React · Neo4j · Supabase pgvector · Hugging Face · Groq · JWT
- 01
Chat UI
frontend/src/api/chat.api.js
- 02
Chat route
api/routes/chat.routes.js
- 03
Customer profile
services/chat.service.js
- 04
Embed question
services/embedding.service.js
- 05
Vector search
db/supabase/driver.js
- 06
Graph enrichment
services/vector.service.js
- 07
Groq answer
services/llm.service.js
DataNeo4jSupabase pgvector
Situation
Banking questions are about relationships: which customer holds which loan, what they asked before, how their records connect. Plain vector search finds similar text but loses those links, so answers come back plausible and unanchored.
Task
Build an assistant that retrieves by meaning and then grounds each result in the graph of customers, loans and conversations.
Action
I modelled users, loans and chats as a Neo4j graph and mirrored their text into Supabase pgvector with MiniLM embeddings from Hugging Face. A question is embedded, matched by similarity above a threshold, and every match is enriched with a Cypher query for its relationships (a loan’s borrower, a user’s loans and chats) before Groq writes the answer. Authentication is JWT with bcrypt-hashed passwords stored in Neo4j, and the admin and audit APIs require the ADMIN role.
// Embed the question, find similar items in pgvector, then enrich each hit from Neo4j.
async function semanticSearch(query, contentType = null, limit = 5) {
const queryEmbedding = await generateEmbedding(query);
const vectorResults = await searchSimilar({
queryEmbedding,
contentType,
limit,
threshold: 0.3,
});
if (vectorResults.length === 0) {
return [];
}
return enrichWithGraphData(vectorResults);
}
Result
A banking assistant whose answers carry the relationships behind them, not just the closest matching text.
Graph + Vector
hybrid retrieval
Neo4j
relationship enrichment
JWT + bcrypt
authentication
Looking back
What I would do differently
- Put JWT middleware on /api/chat and take the role from the verified token, not the request body.
- Mount the audit writer, so every question is actually logged.