Skip to content

HTTP API

The HTTP API is the universal interface contract that connects Tabula Lens frontends and backends. This API follows RESTful conventions and provides a standardized way to query database tables with pagination, filtering, and sorting capabilities.

When the adapter is mounted at /api/tabula-lens (the recommended base path), three sub-routes are exposed:

Endpoint Description
GET /api/tabula-lens/query Query table data with pagination, filtering, and sorting
GET /api/tabula-lens/tables List all available tables
GET /api/tabula-lens/tables/:table Get metadata for a specific table

The @tabula-lens/react component automatically uses all three endpoints based on the path prop you supply (e.g., path="/api/tabula-lens").

GET /api/tabula-lens/query

Parameter Type Required Default Description
table string Yes - Table name to query
page number No 1 Page number (1-indexed)
limit number No 10 Number of rows per page
filter string No - Filter string for searching across text columns
filterColumns string[] No - Specific columns to filter (comma-separated)
sort string No - Sort specification (format: column:direction,column:direction)
columns string[] No - Specific columns to return (comma-separated)
Header Type Required Description
Authorization string No* Authentication token (Bearer token or custom scheme)
Content-Type string No Request content type (typically application/json)
Accept string No Response content type (typically application/json)

*Authentication headers are required if your backend implementation requires authentication.

GET /api/tabula-lens/query?table=users
GET /api/tabula-lens/query?table=users&page=2&limit=25
GET /api/tabula-lens/query?table=users&filter=john
GET /api/tabula-lens/query?table=users&filter=john&filterColumns=name,email
GET /api/tabula-lens/query?table=users&sort=created_at:desc
GET /api/tabula-lens/query?table=users&sort=status:asc,created_at:desc
GET /api/tabula-lens/query?table=users&columns=id,name,email
GET /api/tabula-lens/query?table=users&page=1&limit=25&filter=john&filterColumns=name,email&sort=created_at:desc&columns=id,name,email,created_at
Authorization: Bearer your-token-here

200 OK

{
"data": [
{
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"created_at": "2024-01-01T00:00:00Z"
},
{
"id": 2,
"name": "Jane Smith",
"email": "[email protected]",
"created_at": "2024-01-02T00:00:00Z"
}
],
"columns": ["id", "name", "email", "created_at"],
"pagination": {
"page": 1,
"limit": 25,
"total": 250,
"totalPages": 10
}
}
Field Type Description
data array Array of data records from the queried table
columns string[] Array of column names in the result set
pagination object Pagination metadata
pagination.page number Current page number
pagination.limit number Number of records per page
pagination.total number Total number of records matching the query
pagination.totalPages number Total number of pages available
Status Code Error Type
400 Bad Request (invalid parameters)
401 Unauthorized (authentication failed)
404 Not Found (table not found)
500 Internal Server Error (database or server error)
{
"error": "TABLE_NOT_FOUND",
"message": "Table 'users' does not exist",
"details": {
"table": "users",
"availableTables": ["customers", "products", "orders"]
}
}
Field Type Description
error string Machine-readable error code
message string Human-readable error message
details object Additional error context (optional)

The most common authentication method uses Bearer tokens:

GET /api/tabula-lens/query?table=users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

You can implement custom authentication schemes using the getAuthHeaders prop in the React component:

const getAuthHeaders = async () => ({
'X-API-Key': 'your-api-key-here',
'X-User-ID': 'user-123'
});
GET /api/tabula-lens/query?table=users
X-API-Key: your-api-key-here

If your backend doesn’t require authentication, you can omit authentication headers entirely:

GET /api/tabula-lens/query?table=users

Status Code: 404

Description: The specified table does not exist in the database.

Causes:

  • Table name typo in the request
  • Table doesn’t exist in the database
  • Insufficient database permissions

Example Response:

{
"error": "TABLE_NOT_FOUND",
"message": "Table 'users' does not exist",
"details": {
"table": "users",
"availableTables": ["customers", "products", "orders"]
}
}

Solution: Verify the table name exists in your database and that you have the necessary permissions.


Status Code: 400

Description: Query parameters are invalid or malformed.

Causes:

  • Invalid parameter values
  • Missing required parameters
  • Malformed sort or filter syntax
  • Invalid column names in sort/filter/columns parameters

Example Response:

{
"error": "INVALID_QUERY",
"message": "Invalid query parameter: page must be a positive number",
"details": {
"parameter": "page",
"value": "abc",
"constraint": "must be a positive number"
}
}

Solution: Check that all query parameters are valid and properly formatted.


Status Code: 401

Description: Authentication failed or was not provided.

Causes:

  • Missing authentication headers
  • Invalid or expired authentication token
  • Insufficient permissions for the requested resource

Example Response:

{
"error": "AUTHENTICATION_FAILED",
"message": "Authentication failed: Invalid or expired token",
"details": {
"authMethod": "Bearer",
"reason": "token_expired"
}
}

Solution: Provide valid authentication credentials and ensure they haven’t expired.


Status Code: 500

Description: Database connection or query error.

Causes:

  • Database connection failure
  • Database query syntax error
  • Database constraint violation
  • Insufficient database permissions

Example Response:

{
"error": "DATABASE_ERROR",
"message": "Database error: connection timeout"
}

Solution: Check your database connection, ensure the database is running, and verify your database credentials.


Status Code: 500

Description: An unexpected internal server error.

Causes:

  • Unexpected server error
  • Configuration error
  • Resource exhaustion

Example Response:

{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred"
}

Solution: Contact your system administrator or check server logs for more details.


The table parameter specifies which database table to query.

Requirements:

  • Must be a valid table name in your database
  • Must match exactly (case-sensitive depending on database)
  • Required parameter

Example:

table=users

The page parameter specifies which page of results to return.

Requirements:

  • Must be a positive integer
  • Default: 1
  • 1-indexed (first page is 1)

Example:

page=2

The limit parameter specifies how many records to return per page.

Requirements:

  • Must be a positive integer
  • Default: 10
  • Recommended range: 1-1000

Example:

limit=25

The filter parameter performs a case-insensitive search across text columns.

Requirements:

  • Optional parameter
  • Performs partial matching (ILIKE in PostgreSQL)
  • Searches across all text columns by default

Example:

filter=john

The filterColumns parameter restricts filtering to specific columns.

Requirements:

  • Optional parameter
  • Comma-separated list of column names
  • Only text-based columns are supported
  • If not specified, searches all text columns

Example:

filterColumns=name,email

Combined Example:

filter=john&filterColumns=name,email

The sort parameter specifies sorting order for results.

Format:

sort=column:direction,column:direction

Requirements:

  • Optional parameter
  • Direction can be asc or desc
  • Multiple columns can be specified
  • Column must exist in the table

Examples:

sort=created_at:desc
sort=status:asc,created_at:desc

The columns parameter specifies which columns to return in the result.

Requirements:

  • Optional parameter
  • Comma-separated list of column names
  • Columns must exist in the table
  • If not specified, returns all columns

Example:

columns=id,name,email

Rate limiting is implementation-specific and should be configured on your backend. Consider implementing:

  • Per-IP rate limits
  • Per-user rate limits (if authenticated)
  • Tiered rate limits based on user roles

Example rate limit headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1609459200

Responses can be cached based on the query parameters to improve performance and reduce database load.

Use HTTP caching headers:

Cache-Control: public, max-age=300
ETag: "abc123"

Implement caching in your backend:

  • Redis for distributed caching
  • In-memory caching for single instances
  • Database query caching

Cache keys should be based on the complete query parameters:

cache_key = "tabula_lens:{table}:{page}:{limit}:{filter}:{filterColumns}:{sort}:{columns}"

The HTTP API is designed to be implementation-agnostic:

  • Frontend: Any frontend can consume this API (React is currently supported, others can be implemented by consuming the HTTP API)
  • Backend: Any backend can implement this API (Node.js with Express, Fastify, Koa, Hono, and others are currently supported)
  • Database: Any database can be supported (PostgreSQL, MySQL, SQLite, and SQL Server are currently supported)

This allows you to mix and match implementations while maintaining a stable interface contract.

Returns all available table names in the database.

HTTP Method: GET /api/tabula-lens/tables

Response:

["users", "products", "orders", "customers"]

Returns column metadata for a specific table.

HTTP Method: GET /api/tabula-lens/tables/:table

Example: GET /api/tabula-lens/tables/users

Response:

{
"name": "users",
"columns": [
{ "name": "id", "type": "integer" },
{ "name": "name", "type": "character varying" },
{ "name": "email", "type": "character varying" },
{ "name": "created_at", "type": "timestamp without time zone" }
]
}
Terminal window
# Basic query
curl "http://localhost:3000/api/tabula-lens/query?table=users"
# With authentication
curl -H "Authorization: Bearer your-token" \
"http://localhost:3000/api/tabula-lens/query?table=users&page=1&limit=25"
# With filtering and sorting
curl "http://localhost:3000/api/tabula-lens/query?table=users&filter=john&sort=created_at:desc"
// Basic query
const response = await fetch('http://localhost:3000/api/tabula-lens/query?table=users');
const data = await response.json();
// With authentication
const response = await fetch('http://localhost:3000/api/tabula-lens/query?table=users', {
headers: {
'Authorization': 'Bearer your-token'
}
});
const data = await response.json();
// With all parameters
const params = new URLSearchParams({
table: 'users',
page: '1',
limit: '25',
filter: 'john',
sort: 'created_at:desc'
});
const response = await fetch(`http://localhost:3000/api/tabula-lens/query?${params}`);
const data = await response.json();
import axios from 'axios';
// Basic query
const response = await axios.get('http://localhost:3000/api/tabula-lens/query', {
params: { table: 'users' }
});
// With authentication
const response = await axios.get('http://localhost:3000/api/tabula-lens/query', {
params: { table: 'users', page: 1, limit: 25 },
headers: { 'Authorization': 'Bearer your-token' }
});
// With all parameters
const response = await axios.get('http://localhost:3000/api/tabula-lens/query', {
params: {
table: 'users',
page: 1,
limit: 25,
filter: 'john',
filterColumns: 'name,email',
sort: 'created_at:desc',
columns: 'id,name,email,created_at'
}
});
import requests
# Basic query
response = requests.get('http://localhost:3000/api/tabula-lens/query',
params={'table': 'users'})
data = response.json()
# With authentication
response = requests.get('http://localhost:3000/api/tabula-lens/query',
params={'table': 'users', 'page': 1, 'limit': 25},
headers={'Authorization': 'Bearer your-token'})
data = response.json()
# With all parameters
params = {
'table': 'users',
'page': 1,
'limit': 25,
'filter': 'john',
'filterColumns': 'name,email',
'sort': 'created_at:desc',
'columns': 'id,name,email,created_at'
}
response = requests.get('http://localhost:3000/api/tabula-lens/query', params=params)
data = response.json()
  • Use appropriate page sizes (10-100 records per page)
  • Implement caching for frequently accessed data
  • Use column selection to reduce data transfer
  • Filter and sort at the database level when possible
  • Always use authentication in production
  • Validate and sanitize all input parameters
  • Implement rate limiting to prevent abuse
  • Use HTTPS for all API communications
  • Never expose sensitive data in API responses
  • Always check the HTTP status code
  • Parse error responses to provide meaningful feedback to users
  • Implement retry logic for transient errors
  • Log errors for debugging and monitoring
  • Use pagination for large datasets
  • Display total pages and current page to users
  • Implement infinite scroll or traditional pagination based on use case
  • Cache pagination metadata when possible