@m4rctr3y/reutjs
v1.0.0
Published
TypeScript/JavaScript client library for Reut backend API. Provides a type-safe, fluent API similar to Supabase's JavaScript client.
Maintainers
Readme
@reut/reutjs
TypeScript/JavaScript client library for Reut backend API. Provides a type-safe, fluent API similar to Supabase's JavaScript client but tailored for the Reut API structure.
Features
- 🎯 Type Safety: Full TypeScript support with generics
- 🔗 Fluent API: Chainable query builder methods
- 🔄 Relationship Loading: Easy eager loading with
.with()and relationship aliasing - 📊 Efficient Counts: Get counts without loading data using
.withCount()and.count() - 🔍 Advanced Filtering: Support for relationship filters and operators
- 📄 Pagination: Built-in pagination support
- 🌐 Framework Agnostic: Works in Node.js, React, Vue, Angular, etc.
- 🚀 Axios-based: Reliable HTTP client with interceptors support
Installation
npm install @m4rctr3y/reutjs axiosor
yarn add @m4rctr3y/reutjs axiosor
pnpm add @m4rctr3y/reutjs axiosQuick Start
import { ReutClient } from '@m4rctr3y/reutjs';
// Initialize client
const client = new ReutClient({
url: 'http://localhost:9000'
});
// Define your types
interface Post {
id: number;
title: string;
content: string;
user_id: number;
created_at: string;
updated_at: string;
}
// Query data with relationship aliasing
const posts = await client.from<Post>('posts')
.select(['id', 'title', 'content'])
.where('user_id', 1)
.with([['user', 'user_id']]) // Load user relationship with alias
.withCount([['comments', 'post_id']]) // Get comment count (efficient!)
.page(1)
.limit(10)
.all();
console.log(posts.results);
// Each post will have: { id, title, content, user: {...}, comments_count: 5 }
// Note: user_id is removed from results when using alias
// Get total count (efficient COUNT query)
const totalPosts = await client.from<Post>('posts').count();
console.log(`Total posts: ${totalPosts}`);API Reference
ReutClient
The main client class for interacting with the Reut API.
Constructor
new ReutClient(config: ReutClientConfig)Config Options:
url(string, required): Base URL of the Reut API servertoken(string, optional): Authentication tokenaxiosConfig(AxiosRequestConfig, optional): Additional axios configuration
Example:
const client = new ReutClient({
url: 'http://localhost:9000',
token: 'your-auth-token',
axiosConfig: {
timeout: 5000
}
});Methods
from<T>(tableName: string): Table<T>
Get a table instance for querying.
const postsTable = client.from<Post>('posts');setToken(token: string): void
Set authentication token.
client.setToken('new-token');removeToken(): void
Remove authentication token.
client.removeToken();Table
Represents a database table and provides CRUD operations.
Query Methods (Chainable)
All query methods return the Table instance for chaining.
select(columns: string[]): Table<T>
Select specific columns.
.select(['title', 'user_id'])where(column: string, operatorOrValue: WhereOperator | WhereValue, value?: WhereValue): Table<T>
Add a where condition.
// Simple equality
.where('user_id', 1)
// With operator
.where('age', 'gt', 18)
.where('name', 'like', 'John')whereIn(column: string, values: (string | number)[]): Table<T>
Filter where column value is in array.
.whereIn('user_id', [1, 2, 3])whereNotIn(column: string, values: (string | number)[]): Table<T>
Filter where column value is not in array.
.whereNotIn('status', ['deleted', 'archived'])whereLike(column: string, value: string): Table<T>
Filter with LIKE operator (contains).
.whereLike('title', 'test')whereGt(column: string, value: number): Table<T>
Filter where column is greater than value.
.whereGt('age', 18)whereGte(column: string, value: number): Table<T>
Filter where column is greater than or equal to value.
.whereGte('price', 100)whereLt(column: string, value: number): Table<T>
Filter where column is less than value.
.whereLt('age', 65)whereLte(column: string, value: number): Table<T>
Filter where column is less than or equal to value.
.whereLte('price', 1000)whereNe(column: string, value: WhereValue): Table<T>
Filter where column is not equal to value.
.whereNe('status', 'deleted')whereIsNull(column: string): Table<T>
Filter where column is null.
.whereIsNull('deleted_at')whereIsNotNull(column: string): Table<T>
Filter where column is not null.
.whereIsNotNull('email')with(relationships: WithRelationships): Table<T>
Eager load relationships. Supports both old format (backward compatible) and new format with aliases.
Old Format (Backward Compatible):
.with(['user_id', 'comments'])
// Results: { user_id: {...}, comments: [...] }New Format with Aliases (Recommended):
.with([['user', 'user_id'], ['category', 'category_id']])
// Results: { user: {...}, category: {...} }
// Note: user_id and category_id are removed from resultsBenefits of Aliases:
- Cleaner code:
post.user.nameinstead ofpost.user_id.name - Foreign key columns are automatically removed from results
- More readable and maintainable code
join(joinConfig: JoinConfig): Table<T>
Add a manual join.
.join({
table: 'Users',
localColumn: 'user_id',
refColumn: 'id',
type: 'INNER',
alias: 'user'
})page(page: number): Table<T>
Set page number for pagination.
.page(1)limit(limit: number): Table<T>
Set limit for pagination.
.limit(10)offset(offset: number): Table<T>
Set offset for pagination.
.offset(20)orderBy(column: string, direction: 'asc' | 'desc' = 'asc'): Table<T>
Order results by column.
.orderBy('created_at', 'desc')withCount(relationships: RelationshipAlias[]): Table<T>
Get count of related records (efficient COUNT query, doesn't load data).
.withCount([['comments', 'post_id']])
// Returns: { comments_count: 5 } (no comments array loaded)Example:
const posts = await client.from<Post>('posts')
.select(['id', 'title'])
.withCount([['comments', 'post_id']])
.all();
// Each post will have: { id, title, comments_count: 5 }
// Comments data is NOT loaded, only the countcount(): Promise<number>
Get total count of records matching the query (efficient COUNT query, no data).
// Get total count
const total = await client.from<Post>('posts').count();
// Returns: 10 (just the number)
// Get count with filters
const count = await client.from<Post>('posts')
.where('user_id', 1)
.count();
// Returns: 5 (just the number)Benefits:
- Efficient: Only executes
SELECT COUNT(*), doesn't load data - Fast: Perfect for pagination, statistics, and dashboards
- Works with all query builder methods (where, joins, etc.)
CRUD Methods
all(options?: QueryOptions): Promise<PaginatedResponse<T>>
Get all records with optional query options.
const result = await client.from<Post>('posts')
.select(['title'])
.where('user_id', 1)
.page(1)
.limit(10)
.all();
console.log(result.results); // Array of posts
console.log(result.totalPages); // Total pages
console.log(result.page); // Current page
console.log(result.limit); // Items per page
console.log(result.totalItems); // Total itemsfind(id: string | number, options?: { with?: string[] }): Promise<SingleResponse<T>>
Find a single record by ID.
const post = await client.from<Post>('posts')
.find(1, { with: ['user_id'] });add(data: Partial<T>): Promise<SingleResponse<T>>
Create a new record.
const newPost = await client.from<Post>('posts')
.add({
title: 'New Post',
content: 'Post content',
user_id: 1
});update(id: string | number, data: Partial<T>): Promise<SingleResponse<T>>
Update a record by ID.
const updatedPost = await client.from<Post>('posts')
.update(1, {
title: 'Updated Title'
});delete(id: string | number): Promise<void>
Delete a record by ID.
await client.from<Post>('posts').delete(1);Examples
Basic Query
const posts = await client.from<Post>('posts')
.page(1)
.limit(10)
.all();Select Specific Columns
const posts = await client.from<Post>('posts')
.select(['title', 'user_id'])
.all();Eager Load Relationships
Old Format (Backward Compatible):
const posts = await client.from<Post>('posts')
.with(['user_id'])
.all();
// Results: { user_id: {...} }New Format with Aliases (Recommended):
const posts = await client.from<Post>('posts')
.select(['id', 'title', 'content'])
.with([['user', 'user_id']])
.all();
// Results: { id, title, content, user: {...} }
// Note: user_id is removed from resultsFilter by Column
const posts = await client.from<Post>('posts')
.where('user_id', 1)
.all();Filter by Relationship
const posts = await client.from<Post>('posts')
.where('user.name', 'John Doe')
.all();Complex Query
const posts = await client.from<Post>('posts')
.select(['id', 'title', 'content'])
.where('user_id', 1)
.whereGt('created_at', '2024-01-01')
.with([['user', 'user_id']]) // Load user with alias
.withCount([['comments', 'post_id']]) // Get comment count
.page(1)
.limit(10)
.orderBy('created_at', 'desc')
.all();Get Relationship Counts (Efficient)
// Get posts with comment counts (doesn't load comment data)
const posts = await client.from<Post>('posts')
.select(['id', 'title'])
.withCount([['comments', 'post_id']])
.all();
// Each post: { id, title, comments_count: 5 }Get Total Count
// Get total number of posts
const total = await client.from<Post>('posts').count();
console.log(`Total posts: ${total}`);
// Get count with filters
const filteredCount = await client.from<Post>('posts')
.where('user_id', 1)
.where('status', 'published')
.count();
console.log(`Published posts by user 1: ${filteredCount}`);Create Record
const newPost = await client.from<Post>('posts')
.add({
title: 'New Post',
content: 'Content here',
user_id: 1
});Update Record
const updated = await client.from<Post>('posts')
.update(1, {
title: 'Updated Title'
});Delete Record
await client.from<Post>('posts').delete(1);Type Definitions
PaginatedResponse
interface PaginatedResponse<T> {
results: T[];
totalPages: number;
page: number;
limit: number;
totalItems: number;
}SingleResponse
type SingleResponse<T> = T;WhereOperator
type WhereOperator =
| 'eq' // equals
| 'ne' // not equals
| 'gt' // greater than
| 'gte' // greater than or equal
| 'lt' // less than
| 'lte' // less than or equal
| 'like' // like (contains)
| 'in' // in array
| 'nin' // not in array
| 'is' // is null
| 'isn'; // is not nullRelationshipAlias
type RelationshipAlias = [string, string]; // [alias, fkColumn]
// Example:
[['user', 'user_id'], ['category', 'category_id']]WithRelationships
type WithRelationships = string[] | RelationshipAlias[];
// Old format (backward compatible):
string[] // e.g., ['user_id', 'comments']
// New format (recommended):
RelationshipAlias[] // e.g., [['user', 'user_id'], ['comments', 'post_id']]Recent Updates
v0.1.0 - Latest Features
✨ Relationship Aliasing
- Use cleaner aliases for relationships:
with([['user', 'user_id']]) - Foreign key columns are automatically removed from results
- More readable code:
post.user.nameinstead ofpost.user_id.name - Backward compatible with old
with(['user_id'])format
📊 Efficient Count Queries
withCount(): Get relationship counts without loading data.withCount([['comments', 'post_id']]) // Returns: { comments_count: 5 } (no comments array).count(): Get total count of records matching queryconst total = await client.from('posts').count(); // Returns: 10 (just the number, no data loaded)
🎯 Benefits
- Performance: COUNT queries are much faster than loading all data
- Memory: Reduces memory usage by not loading unnecessary data
- Readability: Cleaner code with relationship aliases
- Backward Compatible: Old code continues to work
Error Handling
The library throws errors for failed requests. Always wrap API calls in try-catch blocks:
try {
const posts = await client.from<Post>('posts').all();
} catch (error) {
console.error('Error fetching posts:', error.message);
}Migration Guide
Upgrading to Relationship Aliases
Before:
const posts = await client.from<Post>('posts')
.select(['id', 'title', 'user_id'])
.with(['user_id'])
.all();
// Results: { id, title, user_id: {...} }After (Recommended):
const posts = await client.from<Post>('posts')
.select(['id', 'title']) // user_id removed from select
.with([['user', 'user_id']])
.all();
// Results: { id, title, user: {...} }
// Note: user_id is automatically removedOld code still works! The old format is fully backward compatible.
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
