Backend Implementation
Backend Implementation
Section titled “Backend Implementation”This guide covers how to integrate Tabula Lens into your Node.js backend with comprehensive documentation for all 15 supported framework adapters.
Installation
Section titled “Installation”npm i @tabula-lens/nodepnpm add @tabula-lens/nodeyarn add @tabula-lens/nodeBasic Setup
Section titled “Basic Setup”import { TabulaLens } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Query dataconst result = await tabulaLens.query({ table: 'users', page: 1, limit: 10,});
console.log(result.data);// => [{ id: 1, name: 'John', email: '[email protected]' }, ...]Configuration Options
Section titled “Configuration Options”TabulaLens Constructor Options
Section titled “TabulaLens Constructor Options”interface TabulaLensOptions { logger?: Logger; logLevel?: 'error' | 'warn' | 'info' | 'debug' | 'silent'; enableQueryLogging?: boolean; enableRequestLogging?: boolean; sensitiveDataMasking?: boolean; logFormat?: 'json' | 'text' | 'pretty';}Basic Configuration
Section titled “Basic Configuration”import { TabulaLens } from '@tabula-lens/node';
// Basic usage with default loggingconst tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Custom log levelconst tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
// Production configurationconst tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'error', logFormat: 'json', sensitiveDataMasking: true,});Framework Adapters
Section titled “Framework Adapters”Tabula Lens supports 15+ Node.js frameworks through dedicated adapters:
Traditional Frameworks
Section titled “Traditional Frameworks”- Express (4.x & 5.x)
- Fastify
- Koa
- Hapi
- Restify
Modern Frameworks
Section titled “Modern Frameworks”- Next.js
- TanStack Start
- Remix
- SvelteKit
Edge Frameworks
Section titled “Edge Frameworks”- Hono
- Elysia
- Fresh
Native
Section titled “Native”- Native Node.js HTTP
Traditional Frameworks
Section titled “Traditional Frameworks”Express (4.x & 5.x)
Section titled “Express (4.x & 5.x)”Peer Dependencies:
npm i express@^4.18.0 || ^5.0.0pnpm add express@^4.18.0 || ^5.0.0yarn add express@^4.18.0 || ^5.0.0Basic Setup:
import express from 'express';import { TabulaLens, expressAdapter } from '@tabula-lens/node';
const app = express();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Create API endpoint using adapterapp.use('/api/tabula-lens', expressAdapter(tabulaLens));
app.listen(3000, () => { console.log('Server running on port 3000');});Advanced Configuration:
import express from 'express';import { TabulaLens, expressAdapter } from '@tabula-lens/node';
const app = express();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info', enableRequestLogging: true,});
// Add middleware for authenticationapp.use('/api/tabula-lens', (req, res, next) => { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: 'Unauthorized' }); } // Validate token here next();});
// Use adapter after authentication middlewareapp.use('/api/tabula-lens', expressAdapter(tabulaLens));
app.listen(3000);Express 5.x Support:
import express from 'express';import { TabulaLens, expressAdapter } from '@tabula-lens/node';
const app = express();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Express 5.x works the same wayapp.use('/api/tabula-lens', expressAdapter(tabulaLens));
app.listen(3000);Fastify
Section titled “Fastify”Peer Dependencies:
npm i fastify@^4.0.0pnpm add fastify@^4.0.0yarn add fastify@^4.0.0Basic Setup:
import Fastify from 'fastify';import { TabulaLens, fastifyAdapter } from '@tabula-lens/node';
const fastify = Fastify();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Create API endpoint using adapterfastify.all('/api/tabula-lens/*', fastifyAdapter(tabulaLens));
fastify.listen({ port: 3000 }, (err) => { if (err) { fastify.log.error(err); process.exit(1); } fastify.log.info('Server running on port 3000');});Advanced Configuration:
import Fastify from 'fastify';import { TabulaLens, fastifyAdapter } from '@tabula-lens/node';
const fastify = Fastify({ logger: true,});
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
// Add authentication hookfastify.addHook('onRequest', async (request, reply) => { if (request.url.startsWith('/api/tabula-lens')) { const token = request.headers.authorization; if (!token) { return reply.status(401).send({ error: 'Unauthorized' }); } }});
// Use adapterfastify.all('/api/tabula-lens/*', fastifyAdapter(tabulaLens));
fastify.listen({ port: 3000 });Peer Dependencies:
npm i koa@^2.14.0pnpm add koa@^2.14.0yarn add koa@^2.14.0npm i @koa/routerpnpm add @koa/routeryarn add @koa/routerBasic Setup:
import Koa from 'koa';import Router from '@koa/router';import { TabulaLens, koaAdapter } from '@tabula-lens/node';
const app = new Koa();const router = new Router();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Create API endpoint using adapterrouter.all('/api/tabula-lens', koaAdapter(tabulaLens));
app.use(router.routes());app.use(router.allowedMethods());
app.listen(3000, () => { console.log('Server running on port 3000');});Advanced Configuration:
import Koa from 'koa';import Router from '@koa/router';import { TabulaLens, koaAdapter } from '@tabula-lens/node';
const app = new Koa();const router = new Router();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
// Add authentication middlewareapp.use(async (ctx, next) => { if (ctx.path.startsWith('/api/tabula-lens')) { const token = ctx.headers.authorization; if (!token) { ctx.status = 401; ctx.body = { error: 'Unauthorized' }; return; } } await next();});
// Use adapterrouter.all('/api/tabula-lens', koaAdapter(tabulaLens));
app.use(router.routes());app.use(router.allowedMethods());
app.listen(3000);Peer Dependencies:
npm i @hapi/hapi@^21.0.0pnpm add @hapi/hapi@^21.0.0yarn add @hapi/hapi@^21.0.0Basic Setup:
import Hapi from '@hapi/hapi';import { TabulaLens, hapiAdapter } from '@tabula-lens/node';
const init = async () => { const server = Hapi.server({ port: 3000, host: 'localhost', });
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string });
// Create API endpoint using adapter server.route({ method: '*', path: '/api/tabula-lens/{p*}', handler: hapiAdapter(tabulaLens), });
await server.start(); console.log('Server running on %s', server.info.uri);};
init();Advanced Configuration:
import Hapi from '@hapi/hapi';import { TabulaLens, hapiAdapter } from '@tabula-lens/node';
const init = async () => { const server = Hapi.server({ port: 3000, host: 'localhost', });
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info', });
// Add authentication pre-handler server.ext('onPreAuth', (request, h) => { if (request.path.startsWith('/api/tabula-lens')) { const token = request.headers.authorization; if (!token) { return h.response({ error: 'Unauthorized' }).code(401); } } return h.continue; });
// Use adapter server.route({ method: '*', path: '/api/tabula-lens/{p*}', handler: hapiAdapter(tabulaLens), });
await server.start(); console.log('Server running on %s', server.info.uri);};
init();Restify
Section titled “Restify”Peer Dependencies:
npm i restify@^11.0.0pnpm add restify@^11.0.0yarn add restify@^11.0.0Basic Setup:
import restify from 'restify';import { TabulaLens, restifyAdapter } from '@tabula-lens/node';
const server = restify.createServer();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Create API endpoint using adapterserver.all('/api/tabula-lens', restifyAdapter(tabulaLens));
server.listen(3000, () => { console.log('Server running on port 3000');});Advanced Configuration:
import restify from 'restify';import { TabulaLens, restifyAdapter } from '@tabula-lens/node';
const server = restify.createServer();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
// Add authentication middlewareserver.use((req, res, next) => { if (req.path().startsWith('/api/tabula-lens')) { const token = req.headers.authorization; if (!token) { res.send(401, { error: 'Unauthorized' }); return next(false); } } next();});
// Use adapterserver.all('/api/tabula-lens', restifyAdapter(tabulaLens));
server.listen(3000);Modern Frameworks
Section titled “Modern Frameworks”Next.js
Section titled “Next.js”Peer Dependencies:
npm i next@latestpnpm add next@latestyarn add next@latestNo additional peer dependencies for Tabula Lens
Section titled “No additional peer dependencies for Tabula Lens”App Router Setup:
import { TabulaLens, createNextRouteHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});const handler = createNextRouteHandler(tabulaLens);
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };Advanced Configuration:
import { TabulaLens, createNextRouteHandler } from '@tabula-lens/node';import { NextRequest } from 'next/server';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info', enableRequestLogging: true,});
const handler = createNextRouteHandler(tabulaLens, { parseBody: true, onRequest: async (request: NextRequest) => { // Custom authentication logic const token = request.headers.get('authorization'); if (!token) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, }); } return null; // Continue to handler },});
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };Pages Router Setup:
import type { NextApiRequest, NextApiResponse } from 'next';import { TabulaLens, createNextRouteHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});const handler = createNextRouteHandler(tabulaLens);
export default async function handlerWrapper( req: NextApiRequest, res: NextApiResponse) { await handler(req, res);}TanStack Start
Section titled “TanStack Start”Peer Dependencies:
npm i @tanstack/react-start@^1.0.0pnpm add @tanstack/react-start@^1.0.0yarn add @tanstack/react-start@^1.0.0Basic Setup:
import { createFileRoute } from '@tanstack/react-router';import { TabulaLens, createTanStackStartHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});export const handler = createTanStackStartHandler(tabulaLens);
export const Route = createFileRoute('/api/tabula-lens')({ loader: async () => { // Optional: Add custom logic return null; },});Advanced Configuration:
import { createFileRoute } from '@tanstack/react-router';import { TabulaLens, createTanStackStartHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens(process.env.DATABASE_URL, { logLevel: 'info',});
export const handler = createTanStackStartHandler(tabulaLens, { onRequest: async (request) => { // Custom authentication const token = request.headers.get('authorization'); if (!token) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, }); } return null; },});
export const Route = createFileRoute('/api/tabula-lens')({ loader: async () => null,});Peer Dependencies:
npm i remix@^2.0.0pnpm add remix@^2.0.0yarn add remix@^2.0.0Basic Setup:
import { TabulaLens, createRemixHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
export const loader = createRemixHandler(tabulaLens);export const action = createRemixHandler(tabulaLens);Advanced Configuration:
import { TabulaLens, createRemixHandler } from '@tabula-lens/node';import { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
const authenticatedHandler = async (args: LoaderFunctionArgs | ActionFunctionArgs) => { const token = args.request.headers.get('authorization'); if (!token) { throw new Response('Unauthorized', { status: 401 }); } return createRemixHandler(tabulaLens)(args);};
export const loader = authenticatedHandler;export const action = authenticatedHandler;SvelteKit
Section titled “SvelteKit”Peer Dependencies:
npm i @sveltejs/kit@^2.0.0pnpm add @sveltejs/kit@^2.0.0yarn add @sveltejs/kit@^2.0.0Basic Setup:
import { TabulaLens, createSvelteKitHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});export const GET = createSvelteKitHandler(tabulaLens);export const POST = createSvelteKitHandler(tabulaLens);Advanced Configuration:
import { TabulaLens, createSvelteKitHandler } from '@tabula-lens/node';import type { RequestEvent } from '@sveltejs/kit';
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
const authenticatedHandler = async (event: RequestEvent) => { const token = event.request.headers.get('authorization'); if (!token) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, }); } return createSvelteKitHandler(tabulaLens)(event);};
export const GET = authenticatedHandler;export const POST = authenticatedHandler;Edge Frameworks
Section titled “Edge Frameworks”Peer Dependencies:
npm i hono@^4.0.0pnpm add hono@^4.0.0yarn add hono@^4.0.0Basic Setup:
import { Hono } from 'hono';import { TabulaLens, honoAdapter } from '@tabula-lens/node';
const app = new Hono();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Create API endpoint using adapterapp.all('/api/tabula-lens/*', honoAdapter(tabulaLens));
export default app;Advanced Configuration:
import { Hono } from 'hono';import { TabulaLens, honoAdapter } from '@tabula-lens/node';
const app = new Hono();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
// Add authentication middlewareapp.use('/api/tabula-lens/*', async (c, next) => { const token = c.req.header('authorization'); if (!token) { return c.json({ error: 'Unauthorized' }, 401); } await next();});
// Use adapterapp.all('/api/tabula-lens/*', honoAdapter(tabulaLens));
export default app;Elysia
Section titled “Elysia”Peer Dependencies:
npm i elysia@^1.0.0pnpm add elysia@^1.0.0yarn add elysia@^1.0.0Basic Setup:
import { Elysia } from 'elysia';import { TabulaLens, elysiaAdapter } from '@tabula-lens/node';
const app = new Elysia();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});
// Create API endpoint using adapterapp.all('/api/tabula-lens/*', elysiaAdapter(tabulaLens));
app.listen(3000);Advanced Configuration:
import { Elysia } from 'elysia';import { TabulaLens, elysiaAdapter } from '@tabula-lens/node';
const app = new Elysia();const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, logLevel: 'info',});
// Add authentication hookapp.onRequest(({ request, set }) => { if (request.url.includes('/api/tabula-lens')) { const token = request.headers.get('authorization'); if (!token) { set.status = 401; return { error: 'Unauthorized' }; } }});
// Use adapterapp.all('/api/tabula-lens/*', elysiaAdapter(tabulaLens));
app.listen(3000);Fresh (Deno)
Section titled “Fresh (Deno)”Peer Dependencies:
# No additional peer dependencies (Deno native)Basic Setup:
import { TabulaLens, freshAdapter } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: Deno.env.get('DATABASE_URL')!, // type is auto-detected from the connection string});
// Create API endpoint using adapterapp.get('/api/tabula-lens', freshAdapter(tabulaLens));app.post('/api/tabula-lens', freshAdapter(tabulaLens));Advanced Configuration:
import { TabulaLens, freshAdapter } from '@tabula-lens/node';
const tabulaLens = new TabulaLens({ url: Deno.env.get('DATABASE_URL')!, logLevel: 'info',});
// Add authentication middlewareconst authMiddleware: Middleware = async (req, ctx) => { if (req.url.includes('/api/tabula-lens')) { const token = req.headers.get('authorization'); if (!token) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, }); } } return ctx.next();};
app.use(authMiddleware);
// Use adapterapp.get('/api/tabula-lens', freshAdapter(tabulaLens));app.post('/api/tabula-lens', freshAdapter(tabulaLens));Native Node.js HTTP
Section titled “Native Node.js HTTP”Peer Dependencies:
# No additional peer dependenciesBasic Setup:
import http from 'http';import { TabulaLens, nativeAdapter } from '@tabula-lens/node';
const tabulaLens = new TabulaLens(process.env.DATABASE_URL);
const server = http.createServer(nativeAdapter(tabulaLens));
server.listen(3000, () => { console.log('Server running on port 3000');});Advanced Configuration:
import http from 'http';import { TabulaLens, nativeAdapter } from '@tabula-lens/node';
const tabulaLens = new TabulaLens(process.env.DATABASE_URL, { logLevel: 'info',});
const server = http.createServer((req, res) => { // Add authentication if (req.url?.startsWith('/api/tabula-lens')) { const token = req.headers.authorization; if (!token) { res.writeHead(401, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Unauthorized' })); return; } }
// Use adapter nativeAdapter(tabulaLens)(req, res);});
server.listen(3000);Security Best Practices
Section titled “Security Best Practices”Authentication
Section titled “Authentication”Always implement proper authentication on your API endpoints:
// Example authentication middlewareconst authenticate = (req, res, next) => { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: 'Unauthorized' }); } // Validate token here next();};Environment Variables
Section titled “Environment Variables”Never commit database credentials to version control:
# .env fileDATABASE_URL=postgresql://user:password@localhost:5432/mydb// Load environment variablesimport dotenv from 'dotenv';dotenv.config();
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // type is auto-detected from the connection string});Database User Permissions
Section titled “Database User Permissions”Use read-only database users when possible:
-- Create read-only userCREATE USER tabula_lens_readonly WITH PASSWORD 'secure_password';GRANT CONNECT ON DATABASE mydb TO tabula_lens_readonly;GRANT USAGE ON SCHEMA public TO tabula_lens_readonly;GRANT SELECT ON ALL TABLES IN SCHEMA public TO tabula_lens_readonly;SSL Connections
Section titled “SSL Connections”Use SSL connections in production:
const tabulaLens = new TabulaLens({ url: process.env.DATABASE_URL, // DATABASE_URL should include sslmode=require // postgresql://user:password@host:port/database?sslmode=require});Error Handling
Section titled “Error Handling”TabulaLensError Class
Section titled “TabulaLensError Class”The package includes a custom error class for error handling:
import { TabulaLens, TabulaLensError } from '@tabula-lens/node';
try { const result = await tabulaLens.query({ table: 'users' });} catch (error) { if (error instanceof TabulaLensError) { console.error('Tabula Lens error:', error.message); console.error('Error code:', error.code); console.error('Error details:', error.details); } else { console.error('Unexpected error:', error); }}Custom Error Handling
Section titled “Custom Error Handling”Implement custom error handling in your routes:
app.use('/api/tabula-lens', async (req, res, next) => { try { await expressAdapter(tabulaLens)(req, res, next); } catch (error) { if (error instanceof TabulaLensError) { res.status(500).json({ error: error.message, code: error.code, }); } else { res.status(500).json({ error: 'Internal server error' }); } }});Monitoring and Observability
Section titled “Monitoring and Observability”Logging Configuration
Section titled “Logging Configuration”Configure logging for monitoring:
const tabulaLens = new TabulaLens(process.env.DATABASE_URL, { logLevel: 'info', logFormat: 'json', enableRequestLogging: true, enableQueryLogging: true, sensitiveDataMasking: true,});Custom Logger Integration
Section titled “Custom Logger Integration”Integrate with your existing logging system:
import winston from 'winston';
const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'combined.log' }), ],});
const tabulaLens = new TabulaLens(process.env.DATABASE_URL, { logger: { debug: (msg) => logger.debug(msg), info: (msg) => logger.info(msg), warn: (msg) => logger.warn(msg), error: (msg) => logger.error(msg), },});Performance Considerations
Section titled “Performance Considerations”Connection Pooling
Section titled “Connection Pooling”Tabula Lens automatically manages connection pooling:
const tabulaLens = new TabulaLens(process.env.DATABASE_URL, { // Connection pooling is handled automatically by Knex.js // You can customize pool size in the connection string: // postgresql://user:password@host:port/database?pool_min=2&pool_max=10});Query Optimization
Section titled “Query Optimization”Optimize queries for better performance:
// Select only needed columnsconst result = await tabulaLens.query({ table: 'users', columns: ['id', 'name', 'email'], // Only select needed columns page: 1, limit: 20,});
// Use appropriate page sizesconst result = await tabulaLens.query({ table: 'users', page: 1, limit: 50, // Balance between data transfer and number of requests});Testing
Section titled “Testing”Testing with Framework Adapters
Section titled “Testing with Framework Adapters”Test your Tabula Lens integration:
import { TabulaLens } from '@tabula-lens/node';import request from 'supertest';import express from 'express';
describe('Tabula Lens Integration', () => { let app: express.Express; let tabulaLens: TabulaLens;
beforeAll(() => { tabulaLens = new TabulaLens('postgresql://test:test@localhost:5432/test'); app = express(); app.use('/api/tabula-lens', expressAdapter(tabulaLens)); });
afterAll(async () => { await tabulaLens.close(); });
it('should query data', async () => { const response = await request(app) .get('/api/tabula-lens?table=users&page=1&limit=10') .expect(200);
expect(response.body).toHaveProperty('data'); expect(response.body).toHaveProperty('columns'); expect(response.body).toHaveProperty('pagination'); });});Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”Connection Issues:
// Verify your DATABASE_URLconsole.log('DATABASE_URL:', process.env.DATABASE_URL);
// Test connectiontry { const tables = await tabulaLens.getTables(); console.log('Connected successfully. Tables:', tables);} catch (error) { console.error('Connection failed:', error);}Adapter Issues:
// Ensure adapter is properly importedimport { expressAdapter } from '@tabula-lens/node';
// Verify adapter is used correctlyapp.use('/api/tabula-lens', expressAdapter(tabulaLens));Performance Issues:
// Enable query logging to diagnose performanceconst tabulaLens = new TabulaLens(process.env.DATABASE_URL, { logLevel: 'debug', enableQueryLogging: true,});Tabula Lens provides comprehensive framework adapter support for seamless integration with your existing Node.js backend infrastructure.