Everyone wants to build a "Chat with your Data" feature right now.
It sounds amazing in theory: let users type "Show me all laptops under $500" and have an LLM automatically query your database and return the results.
But in practice? It is a security nightmare.
If you blindly pass natural language to an LLM (like Gemini, OpenAI, or Claude) and ask it to write raw SQL or Prisma queries, you are exposing your application to AI-driven injection attacks and catastrophic hallucinations.
LLMs will confidently hallucinate column names that don't exist, bypass your application's security protocols, or worse, generate queries that expose sensitive data like passwordHash or stripe_customer_id.
I got tired of writing the same massive, boilerplate-heavy validation layers to prevent this, so I built an open-source solution.
Enter prisma-safe-ai-query π‘οΈ
prisma-safe-ai-query is a lightweight, enterprise-grade utility that acts as a secure bridge between Google Gemini and your Prisma ORM.
It bundles @google/genai and zod directly to eliminate backend boilerplate. You pass in a user prompt, and the package handles the AI orchestration, schema enforcement, and injection prevention automatically.
The Core Capabilities:
Zero Hallucinated Columns: Forces the AI to output JSON that perfectly matches a strict Zod schema. If the AI makes up a field, the engine catches it before Prisma ever sees it.
Dynamic Table Routing: The AI can intelligently decide which table to query (e.g., routing "Show me admins" to the User table, and "Show me orders" to the Order table).
Defense-in-Depth Security: Built-in allowlists and blocklists ensure the AI can never query sensitive columns, no matter how hard a user tries to prompt-inject it.
π Let's Build a Secure Chat API
Here is how you can implement a completely secure, dynamic AI-to-database chat route in Next.js or Express in just a few lines of code.
- Install the package
npm install prisma-safe-ai-query
(Note: requires @prisma/client as a peer dependency)
- The Backend Controller
Instead of manually configuring the Gemini SDK, defining structured output schemas, and writing Zod validation, you just initialize the PrismaGeminiAgent.
import { PrismaGeminiAgent } from 'prisma-safe-ai-query';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Automatically picks up GEMINI_API_KEY from your .env
const aiAgent = new PrismaGeminiAgent();
export async function POST(req: Request) {
const { userPrompt } = await req.json(); // e.g. "Show me active users in London"
try {
// 1. Generate the safe query & dynamically route the table
const { targetTable, where } = await aiAgent.generateSafeQuery(userPrompt, {
dbContext: "Available tables are user, product, and order.", // Context helps Gemini route correctly
blockList: ['password', 'credit_card_number'] // π Protect sensitive columns!
});
// 2. Validate the requested table exists in your Prisma schema
if (!targetTable || !(targetTable in prisma)) {
return new Response(JSON.stringify({ error: "Invalid table requested." }), { status: 400 });
}
// 3. Execute dynamically across ANY table
// This runs the equivalent of prisma.user.findMany({ where: ... })
const dbResults = await (prisma as any)[targetTable].findMany({
where: where,
take: 10 // Always limit AI results!
});
return new Response(JSON.stringify({ data: dbResults }), { status: 200 });
} catch (error) {
// Catches Zod errors, blocked fields, or Gemini failures safely
return new Response(JSON.stringify({ error: error.message }), { status: 400 });
}
}
π Controlling AI Access (Allowlist vs. Blocklist)
The most important part of this package is the security configuration object passed to generateSafeQuery(). You shouldn't trust an LLM with full access to your database schema.
You have two ways to lock it down:
- The Blocklist (Deny specific fields) Use this if you want the AI to access everything except sensitive columns.
const { targetTable, where } = await aiAgent.generateSafeQuery(userPrompt, {
blockList: ['passwordHash', 'apiKey', 'billingDetails']
});
- The Allowlist (Strict Mode) Use this for maximum security. The AI is only allowed to filter by the exact columns you explicitly permit. Any other field throws an immediate error.
const { targetTable, where } = await aiAgent.generateSafeQuery(userPrompt, {
allowList: ['status', 'role', 'city']
});
Try it out!
Building AI tooling shouldn't mean sacrificing database security. I built this to bridge the gap between rapid AI prototyping and production-ready safety.
Check out the package on npm here:
π npmjs.com/package/prisma-safe-ai-query
If you find this useful for your projects, I would love to hear your feedback in the comments, and PRs/issues on the GitHub repo are always welcome!
Happy (and safe) coding! π
Top comments (0)