npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.

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 axios

or

yarn add @m4rctr3y/reutjs axios

or

pnpm add @m4rctr3y/reutjs axios

Quick 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 server
  • token (string, optional): Authentication token
  • axiosConfig (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 results

Benefits of Aliases:

  • Cleaner code: post.user.name instead of post.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 count
count(): 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 items
find(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 results

Filter 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 null

RelationshipAlias

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.name instead of post.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 query
    const 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 removed

Old code still works! The old format is fully backward compatible.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.