Overview
This page covers advanced configuration options for customizing Quail BI beyond basic environment variables.
Next.js Configuration
Quail’s Next.js configuration is defined in next.config.mjs:
import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev' ;
if ( process . env . NODE_ENV === 'development' ) {
await setupDevPlatform ();
}
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone' ,
reactStrictMode: true ,
images: {
domains: [ 'media.brand.dev' ],
},
experimental: {
optimizePackageImports: [ 'recharts' , '@radix-ui/react-icons' ],
},
};
export default nextConfig ;
Output Mode
The standalone output mode creates a minimal Node.js server with all required dependencies. This is ideal for containerized deployments.
Image Domains
images : {
domains : [ 'media.brand.dev' ],
}
Add domains that host images you want to use with Next.js <Image> component:
images : {
domains : [
'media.brand.dev' ,
'your-cdn.com' ,
'avatars.githubusercontent.com' ,
],
}
Package Import Optimization
experimental : {
optimizePackageImports : [ 'recharts' , '@radix-ui/react-icons' ],
}
This reduces bundle size by only importing used components from large libraries.
TypeScript Configuration
TypeScript settings are in tsconfig.json:
{
"compilerOptions" : {
"target" : "ES2017" ,
"lib" : [ "dom" , "dom.iterable" , "esnext" ],
"allowJs" : true ,
"skipLibCheck" : true ,
"strict" : true ,
"noEmit" : true ,
"esModuleInterop" : true ,
"module" : "esnext" ,
"moduleResolution" : "bundler" ,
"resolveJsonModule" : true ,
"isolatedModules" : true ,
"jsx" : "preserve" ,
"incremental" : true ,
"plugins" : [
{
"name" : "next"
}
],
"paths" : {
"@/*" : [ "./*" ]
}
},
"include" : [ "next-env.d.ts" , "**/*.ts" , "**/*.tsx" , ".next/types/**/*.ts" ],
"exclude" : [ "node_modules" ]
}
Key Settings
Enables all strict type-checking options. Recommended for catching bugs early.
"paths" : {
"@/*" : [ "./*" ]
}
Allows importing with @/ prefix: import { Button } from '@/components/ui/button' ;
instead of: import { Button } from '../../components/ui/button' ;
Middleware Configuration
The middleware.ts file handles authentication and request processing:
import { createServerClient } from '@supabase/ssr' ;
import { NextResponse , type NextRequest } from 'next/server' ;
export async function middleware ( request : NextRequest ) {
let response = NextResponse . next ({
request: {
headers: request . headers ,
},
});
const supabase = createServerClient (
process . env . NEXT_PUBLIC_SUPABASE_URL ! ,
process . env . NEXT_PUBLIC_SUPABASE_ANON_KEY ! ,
{
cookies: {
get ( name : string ) {
return request . cookies . get ( name )?. value ;
},
set ( name : string , value : string , options : any ) {
response . cookies . set ({
name ,
value ,
... options ,
});
},
remove ( name : string , options : any ) {
response . cookies . set ({
name ,
value: '' ,
... options ,
});
},
},
}
);
const { data : { session } } = await supabase . auth . getSession ();
// Protect authenticated routes
if ( ! session && request . nextUrl . pathname . startsWith ( '/app' )) {
const redirectUrl = new URL ( '/login' , request . url );
redirectUrl . searchParams . set ( 'redirect' , request . nextUrl . pathname );
return NextResponse . redirect ( redirectUrl );
}
return response ;
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.* \\ .(?:svg|png|jpg|jpeg|gif|webp)$).*)' ,
],
};
Customizing Protected Routes
Add or remove protected path patterns:
// Protect all /app and /dashboard routes
if ( ! session && (
request . nextUrl . pathname . startsWith ( '/app' ) ||
request . nextUrl . pathname . startsWith ( '/dashboard' )
)) {
// Redirect to login
}
Customizing Matcher
The matcher determines which routes run middleware:
export const config = {
matcher: [
// Run on all routes except:
'/((?!_next/static|_next/image|favicon.ico|api/public).*)' ,
],
};
Tailwind Configuration
Tailwind CSS is configured in tailwind.config.ts:
import type { Config } from 'tailwindcss' ;
import tailwindcssAnimate from 'tailwindcss-animate' ;
const config : Config = {
darkMode: [ 'class' ],
content: [
'./pages/**/*.{ts,tsx}' ,
'./components/**/*.{ts,tsx}' ,
'./app/**/*.{ts,tsx}' ,
'./src/**/*.{ts,tsx}' ,
],
theme: {
extend: {
colors: {
border: 'hsl(var(--border))' ,
input: 'hsl(var(--input))' ,
ring: 'hsl(var(--ring))' ,
background: 'hsl(var(--background))' ,
foreground: 'hsl(var(--foreground))' ,
primary: {
DEFAULT: 'hsl(var(--primary))' ,
foreground: 'hsl(var(--primary-foreground))' ,
},
// ... other colors
},
borderRadius: {
lg: 'var(--radius)' ,
md: 'calc(var(--radius) - 2px)' ,
sm: 'calc(var(--radius) - 4px)' ,
},
},
},
plugins: [ tailwindcssAnimate ],
};
export default config ;
Custom Theme Colors
Modify colors in app/globals.css:
@layer base {
:root {
--background : 0 0 % 100 % ;
--foreground : 222.2 84 % 4.9 % ;
--primary : 222.2 47.4 % 11.2 % ;
/* ... other variables */
}
.dark {
--background : 222.2 84 % 4.9 % ;
--foreground : 210 40 % 98 % ;
--primary : 210 40 % 98 % ;
/* ... other variables */
}
}
MongoDB Configuration
MongoDB client configuration in lib/config/mongodb.ts:
import { MongoClient } from 'mongodb' ;
if ( ! process . env . MONGODB_URI ) {
throw new Error ( 'Please add your MongoDB URI to .env.local' );
}
const uri = process . env . MONGODB_URI ;
const options = {
maxPoolSize: 10 ,
minPoolSize: 5 ,
maxIdleTimeMS: 30000 ,
};
let client : MongoClient ;
let clientPromise : Promise < MongoClient >;
if ( process . env . NODE_ENV === 'development' ) {
// In development, use a global variable to preserve the client across hot reloads
let globalWithMongo = global as typeof globalThis & {
_mongoClientPromise ?: Promise < MongoClient >;
};
if ( ! globalWithMongo . _mongoClientPromise ) {
client = new MongoClient ( uri , options );
globalWithMongo . _mongoClientPromise = client . connect ();
}
clientPromise = globalWithMongo . _mongoClientPromise ;
} else {
// In production, create a new client
client = new MongoClient ( uri , options );
clientPromise = client . connect ();
}
export default clientPromise ;
Connection Pool Settings
const options = {
maxPoolSize: 10 , // Maximum connections
minPoolSize: 5 , // Minimum connections
maxIdleTimeMS: 30000 , // Close idle connections after 30s
};
Adjust pool sizes based on your traffic:
Low traffic: maxPoolSize: 5, minPoolSize: 2
High traffic: maxPoolSize: 50, minPoolSize: 10
AI Provider Configuration
Quail configures Azure AI inline in each API route:
app/app/api/biChat/route.ts
import { createAzure } from '@ai-sdk/azure' ;
// Azure OpenAI configuration
const azure = createAzure ({
resourceName: process . env . NEXT_PUBLIC_AZURE_RESOURCE_NAME ! ,
apiKey: process . env . NEXT_PUBLIC_AZURE_API_KEY ! ,
});
// Model selection is dynamic based on request parameters
Switching AI Providers
OpenAI
Anthropic Claude
Google Gemini
import { openai } from '@ai-sdk/openai' ;
export const model = openai ( 'gpt-4o' );
Set OPENAI_API_KEY in environment. import { anthropic } from '@ai-sdk/anthropic' ;
export const model = anthropic ( 'claude-3-5-sonnet-20241022' );
Set ANTHROPIC_API_KEY in environment. import { google } from '@ai-sdk/google' ;
export const model = google ( 'models/gemini-1.5-pro' );
Set GOOGLE_GENERATIVE_AI_API_KEY in environment.
Component Library Configuration
Components are configured in components.json:
{
"$schema" : "https://ui.shadcn.com/schema.json" ,
"style" : "default" ,
"rsc" : true ,
"tsx" : true ,
"tailwind" : {
"config" : "tailwind.config.ts" ,
"css" : "app/globals.css" ,
"baseColor" : "slate" ,
"cssVariables" : true
},
"aliases" : {
"components" : "@/components" ,
"utils" : "@/lib/utils"
}
}
This configuration is used by the shadcn/ui CLI for adding components.
Build Configuration
Customize the build process in package.json:
{
"scripts" : {
"dev" : "next dev --turbopack" ,
"build" : "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/" ,
"start" : "next start" ,
"lint" : "next lint"
}
}
Turbopack (Development)
"dev" : "next dev --turbopack"
Turbopack is Next.js’s new bundler, providing faster hot module replacement. Remove --turbopack to use webpack.
Standalone Build
"build" : "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/"
Creates a self-contained build in .next/standalone/ with:
Minimal Node.js server
All required dependencies
Static assets
Public files
Enable Caching
Add Redis for caching query results:
import { Redis } from '@upstash/redis' ;
const redis = new Redis ({
url: process . env . REDIS_URL ! ,
token: process . env . REDIS_TOKEN ! ,
});
export async function cacheQuery (
key : string ,
data : any ,
ttl : number = 300 // 5 minutes
) {
await redis . set ( key , JSON . stringify ( data ), { ex: ttl });
}
export async function getCachedQuery ( key : string ) {
const data = await redis . get ( key );
return data ? JSON . parse ( data ) : null ;
}
Enable Compression
Add compression middleware:
import { NextResponse } from 'next/server' ;
import type { NextRequest } from 'next/server' ;
export function middleware ( request : NextRequest ) {
const response = NextResponse . next ();
// Enable compression
response . headers . set ( 'Content-Encoding' , 'gzip' );
return response ;
}
Database Indexing
Add indexes to MongoDB collections:
// In MongoDB shell or via driver
db . connections . createIndex ({ userId: 1 });
db . dashboards . createIndex ({ userId: 1 , createdAt: - 1 });
db . charts . createIndex ({ dashboardId: 1 });
Environment Variables Configure all environment variables
Security Security configuration and best practices
AI Integration Configure AI providers and features
Troubleshooting Fix configuration issues