Clerk Integration
Clerk Integration
Section titled “Clerk Integration”Clerk is a complete authentication and user management platform. This guide shows how to protect your Tabula Lens endpoints using Clerk middleware and pass session tokens to the DatabaseViewer component.
Prerequisites
Section titled “Prerequisites”Install the appropriate Clerk package for your framework:
# # Next.jsnpm i @clerk/nextjs# # Next.jspnpm add @clerk/nextjs# # Next.jsyarn add @clerk/nextjs# # Expressnpm i @clerk/express# # Expresspnpm add @clerk/express# # Expressyarn add @clerk/expressYou’ll also need a Clerk account with CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY set in your environment.
Backend Setup
Section titled “Backend Setup”Next.js App Router
Section titled “Next.js App Router”Clerk requires its middleware to run on every request so the auth() helper has access to the session. Add clerkMiddleware() to your middleware.ts:
import { clerkMiddleware } from '@clerk/nextjs/server';
export default clerkMiddleware();
export const config = { matcher: [ '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', '/(api|trpc)(.*)', ],};Then protect the Tabula Lens route handler:
import { auth } from '@clerk/nextjs/server';import { TabulaLens, createNextRouteHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens(process.env.DATABASE_URL!);const handler = createNextRouteHandler(tabulaLens);
export async function GET(request: Request) { const { userId } = await auth(); if (!userId) return new Response('Unauthorized', { status: 401 }); return handler(request);}Express
Section titled “Express”Apply clerkMiddleware() globally, then guard the Tabula Lens route with a manual getAuth() check:
import express from 'express';import { clerkMiddleware, getAuth } from '@clerk/express';import { TabulaLens, expressAdapter } from '@tabula-lens/node';
const app = express();const tabulaLens = new TabulaLens(process.env.DATABASE_URL!);
// Apply Clerk middleware globallyapp.use(clerkMiddleware());
// Protect the Tabula Lens routeapp.use('/api/tabula-lens', (req, res, next) => { const { userId } = getAuth(req); if (!userId) return res.status(401).json({ error: 'Unauthorized' }); next();}, expressAdapter(tabulaLens));Frontend Integration
Section titled “Frontend Integration”Use the useAuth hook to retrieve a session token and pass it via getAuthHeaders:
'use client';import { useAuth } from '@clerk/nextjs';import { DatabaseViewer } from '@tabula-lens/react';
export function TabulaLensViewer() { const { getToken, isLoaded, isSignedIn } = useAuth();
if (!isLoaded) return <div>Loading...</div>; if (!isSignedIn) return <div>Sign in to view data</div>;
return ( <DatabaseViewer path="/api/tabula-lens" getAuthHeaders={async () => { const token = await getToken(); return { Authorization: `Bearer ${token}` }; }} /> );}Important Notes
Section titled “Important Notes”- Use
@clerk/express, not the deprecated@clerk/clerk-sdk-nodepackage. - Don’t use
requireAuth()for API routes — it’s deprecated. The manualgetAuth()check shown above gives you explicit control over the error response. clerkMiddleware()inmiddleware.tsis required for theauth()helper to work inside App Router route handlers. Without it,auth()will return an empty object.getToken()works for both same-origin and cross-origin requests. For same-origin Next.js routes Clerk can also authenticate via cookie, but callinggetToken()explicitly is more reliable.- JWT templates are for third-party integrations (e.g., Supabase, Firebase). Use plain
getToken()— no template argument needed — for Tabula Lens.
Troubleshooting
Section titled “Troubleshooting”401 returned from the route handler despite being signed in
Confirm clerkMiddleware() is configured in middleware.ts and that its matcher covers /api routes. Without the middleware, auth() cannot read the session.
getToken() returns null on the frontend
The user is not signed in, or the Clerk provider has not finished loading. Always check isLoaded and isSignedIn before rendering DatabaseViewer.
Express: getAuth(req).userId is undefined
Ensure app.use(clerkMiddleware()) is registered before your protected routes. Clerk must parse the request before getAuth can access it.