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

@themarblejar/stark-fetch

v0.1.6

Published

A modern, flexible data fetching library for JavaScript/TypeScript applications with a clean, fluent API.

Readme

Stark Fetch

A modern, flexible data fetching library for JavaScript/TypeScript applications with a clean, fluent API.

Overview

Stark Fetch provides a simple yet powerful way to interact with your backend data. It offers:

  • A fluent, chainable API for building queries
  • Type-safe operations with TypeScript support
  • Flexible data fetching with filtering, sorting, and pagination
  • Support for relational data
  • Express adapter for easy API creation

Architecture

Stark Fetch follows a clean, layered architecture with a clear separation of concerns:

                  ┌─────────────────┐
                  │                 │
                  │  Core Module    │
                  │                 │
                  └─────────────────┘
                          ▲
                          │
                          │
          ┌───────────────┼───────────────┐
          │               │               │
          │               │               │
          ▼               ▼               ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│                 │ │                 │ │                 │
│  Database       │ │  Adapters       │ │  Utils          │
│  Module         │ │  Module         │ │  Module         │
│                 │ │                 │ │                 │
└─────────────────┘ └─────────────────┘ └─────────────────┘

Structural Flow Diagram

When a client method is called, the following flow occurs:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│             │     │             │     │             │     │             │
│  Application│     │ StarkClient │     │ QueryBuilder│     │DatabaseClient│
│    Code     │────▶│   (Core)    │────▶│   (Core)    │────▶│   (Core)    │
│             │     │             │     │             │     │             │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘
                          │                                        │
                          │                                        │
                          ▼                                        ▼
                    ┌─────────────┐                         ┌─────────────┐
                    │             │                         │             │
                    │ DataService │◀────────────────────────│    Knex     │
                    │ (Database)  │                         │ (Database)  │
                    │             │                         │             │
                    └─────────────┘                         └─────────────┘

Flow Explanation:

  1. Application Code calls methods on the StarkClient (e.g., client.table('users').getMany())
  2. StarkClient uses the QueryBuilder to construct the query parameters
  3. QueryBuilder builds and stores query parameters (table, filter, sort, etc.)
  4. StarkClient calls the appropriate method on DataService
  5. DataService delegates to DatabaseClient for CRUD operations
  6. DatabaseClient executes the database operations using Knex
  7. Knex performs the actual SQL queries
  8. Results flow back up the chain to the Application Code

This architecture ensures:

  • Clear separation of concerns
  • Single responsibility for each component
  • Centralized database logic in the DatabaseClient
  • Simplified service layer that focuses on business logic
  • Consistent error handling throughout the stack

Detailed Sequence Diagram

Here's a detailed sequence diagram showing what happens when client.table('users').filter('role=admin').getMany() is called:

┌───────────┐          ┌────────────┐          ┌─────────────┐          ┌──────────────┐          ┌────────────┐          ┌─────┐
│Application│          │StarkClient │          │QueryBuilder │          │ DataService  │          │DatabaseClient          │Knex │
│           │          │            │          │             │          │              │          │            │          │     │
└─────┬─────┘          └──────┬─────┘          └──────┬──────┘          └──────┬───────┘          └──────┬─────┘          └──┬──┘
      │                       │                       │                        │                         │                    │
      │ table('users')        │                       │                        │                         │                    │
      │──────────────────────▶│                       │                        │                         │                    │
      │                       │ table('users')        │                        │                         │                    │
      │                       │──────────────────────▶│                        │                         │                    │
      │                       │                       │                        │                         │                    │
      │ filter('role=admin')  │                       │                        │                         │                    │
      │──────────────────────▶│                       │                        │                         │                    │
      │                       │ filter('role=admin')  │                        │                         │                    │
      │                       │──────────────────────▶│                        │                         │                    │
      │                       │                       │                        │                         │                    │
      │ getMany()             │                       │                        │                         │                    │
      │──────────────────────▶│                       │                        │                         │                    │
      │                       │ getTableName()        │                        │                         │                    │
      │                       │──────────────────────▶│                        │                         │                    │
      │                       │ 'users'               │                        │                         │                    │
      │                       │◀──────────────────────│                        │                         │                    │
      │                       │                       │                        │                         │                    │
      │                       │ getFilter()           │                        │                         │                    │
      │                       │──────────────────────▶│                        │                         │                    │
      │                       │ 'role=admin'          │                        │                         │                    │
      │                       │◀──────────────────────│                        │                         │                    │
      │                       │                       │                        │                         │                    │
      │                       │ getMany('users', {...})                        │                         │                    │
      │                       │───────────────────────────────────────────────▶│                         │                    │
      │                       │                       │                        │ getMany('users', {...}) │                    │
      │                       │                       │                        │─────────────────────────▶                    │
      │                       │                       │                        │                         │ from('users')      │
      │                       │                       │                        │                         │───────────────────▶│
      │                       │                       │                        │                         │                    │
      │                       │                       │                        │                         │ where('role=admin')│
      │                       │                       │                        │                         │───────────────────▶│
      │                       │                       │                        │                         │                    │
      │                       │                       │                        │                         │ SQL query executed │
      │                       │                       │                        │                         │◀───────────────────│
      │                       │                       │                        │                         │                    │
      │                       │                       │                        │                         │ Format response    │
      │                       │                       │                        │                         │─────┐              │
      │                       │                       │                        │                         │◀────┘              │
      │                       │                       │                        │ Response                │                    │
      │                       │                       │                        │◀─────────────────────────                    │
      │                       │ Response              │                        │                         │                    │
      │                       │◀───────────────────────────────────────────────│                         │                    │
      │                       │                       │                        │                         │                    │
      │                       │ reset()               │                        │                         │                    │
      │                       │──────────────────────▶│                        │                         │                    │
      │                       │                       │                        │                         │                    │
      │ Response              │                       │                        │                         │                    │
      │◀──────────────────────│                       │                        │                         │                    │
      │                       │                       │                        │                         │                    │
┌─────┴─────┐          ┌──────┴─────┐          ┌──────┴──────┐          ┌──────┴───────┐          ┌──────┴─────┐          ┌──┴──┐
│Application│          │StarkClient │          │QueryBuilder │          │ DataService  │          │DatabaseClient          │Knex │
│           │          │            │          │             │          │              │          │            │          │     │
└───────────┘          └────────────┘          └─────────────┘          └──────────────┘          └────────────┘          └─────┘

This sequence diagram illustrates:

  1. How method chaining works with the QueryBuilder
  2. How the StarkClient retrieves query parameters from the QueryBuilder
  3. How the DataService delegates to the DatabaseClient
  4. How the DatabaseClient uses Knex to execute the query
  5. How the response flows back through the layers

Core Components

  • Core Module: Contains the essential interfaces, types, and implementations

    • StarkClient: Main entry point for application code, provides a fluent API
    • QueryBuilder: Builds and manages query parameters
    • DatabaseClient: Handles all database operations and CRUD functionality
    • Types: Core type definitions
  • Database Module: Provides service layer and data access

    • DataService: Delegates to DatabaseClient for CRUD operations
    • Auth Service: Handles authentication operations
  • Adapters Module: Provides integration with frameworks

    • ExpressAdapter: Creates REST APIs with Express
  • Utils Module: Provides utility functions

    • ErrorHandling: Error classes and utilities
    • Validation: Input validation utilities

Installation

npm install stark-fetch
# or
yarn add stark-fetch

Column Naming Conventions

Stark Fetch handles the conversion between different naming conventions:

  1. Application Code: When using the Stark Fetch API, you should use lowerCamelCase for all column names (e.g., firstName, createdOn, updatedOn).

  2. Database Storage: In the database, all column names are stored in lowercase (e.g., firstname, createdon, updatedon).

  3. Automatic Columns: Every table automatically has three standard columns:

    • id (primary key)
    • createdOn (creation timestamp)
    • updatedOn (last update timestamp)

Stark Fetch handles the conversion between these formats automatically, so you can always use the lowerCamelCase format in your application code, regardless of how the data is stored in the database.

// Example using lowerCamelCase column names
const result = await client
  .table('users')
  .filter('createdOn > "2023-01-01"')
  .sort('lastName', 'asc')
  .select('id', 'firstName', 'lastName', 'email', 'createdOn')
  .getMany();

Basic Usage

import { createClient } from 'stark-fetch';

// Create a client with database configuration
const client = createClient({
  client: 'pg',
  connection: {
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'postgres',
    password: process.env.DB_PASSWORD || 'password',
    database: process.env.DB_NAME || 'myapp'
  },
  pool: { min: 0, max: 10 }
});

// Fetch users with filtering and pagination
async function getUsers() {
  try {
    const result = await client
      .table('users')
      .filter('role=admin')
      .sort('createdOn', 'desc')
      .paginate(1, 10)
      .getMany();
    
    console.log(`Found ${result.meta.total} users`);
    return result.data;
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
}

// Get a single user by ID with relations
async function getUser(id: string) {
  try {
    const result = await client
      .table('users')
      .withRelations(['posts', 'comments'])
      .select('id', 'firstName', 'lastName', 'email', 'role')
      .getOne(id);
    
    if (!result.data) {
      throw new Error(`User with ID ${id} not found`);
    }
    
    return result.data;
  } catch (error) {
    console.error(`Error fetching user ${id}:`, error);
    throw error;
  }
}

// Create a new user
async function createUser(userData: { 
  firstName: string; 
  lastName: string; 
  email: string; 
  role: string;
}) {
  try {
    const result = await client
      .table('users')
      .create(userData);
    
    console.log(`Created user with ID: ${result.data.id}`);
    return result.data;
  } catch (error) {
    console.error('Error creating user:', error);
    throw error;
  }
}

// Update a user
async function updateUser(id: string, userData: Partial<{
  firstName: string;
  lastName: string;
  email: string;
  role: string;
}>) {
  try {
    const result = await client
      .table('users')
      .update(id, userData);
    
    console.log(`Updated user with ID: ${id}`);
    return result.data;
  } catch (error) {
    console.error(`Error updating user ${id}:`, error);
    throw error;
  }
}

// Delete a user
async function deleteUser(id: string) {
  try {
    await client
      .table('users')
      .delete(id);
    
    console.log(`Deleted user with ID: ${id}`);
    return true;
  } catch (error) {
    console.error(`Error deleting user ${id}:`, error);
    throw error;
  }
}

Express API Integration

Stark Fetch makes it easy to create REST APIs with Express:

import express from 'express';
import { createDataRouter, createAuthRouter } from 'stark-fetch/database/adapters';
import { createClient } from 'stark-fetch';

const app = express();
app.use(express.json());

// Create a database configuration
const dbConfig = {
  client: 'pg',
  connection: {
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'postgres',
    password: process.env.DB_PASSWORD || 'password',
    database: process.env.DB_NAME || 'myapp'
  }
};

// Create a Stark client
const client = createClient(dbConfig);

// Get the database service
const dataService = client.getDatabaseClient().getService();

// Optional middleware for authentication
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Authentication required' });
  }
  
  try {
    // Verify token and set user on request
    req.user = { id: 'user-id', role: 'admin' };
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
};

// Optional middleware for metrics
const metricsMiddleware = (req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`);
  });
  
  next();
};

// Create and use data router with middleware
const dataRouter = createDataRouter(dataService, {
  authMiddleware: [authMiddleware],
  metricsMiddleware
});

app.use('/api', dataRouter);

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err);
  
  if (err.statusCode) {
    return res.status(err.statusCode).json({ error: err.message });
  }
  
  return res.status(500).json({ error: 'Internal server error' });
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

This creates the following REST API endpoints:

  • GET /api/:tableName - Get multiple records
  • GET /api/:tableName/:id - Get a single record by ID
  • POST /api/:tableName - Create a new record
  • PUT /api/:tableName/:id - Update a record by ID
  • DELETE /api/:tableName/:id - Delete a record by ID

Advanced Features

Query Building

Stark Fetch now uses a powerful SQL query builder that provides fine-grained control over your database queries:

// Complex filtering with multiple conditions
client
  .table('users')
  .filter('age>30 AND (role=admin OR role=editor)')
  .getMany();

// Multiple sorting with different directions
// Use - prefix for descending order, + or no prefix for ascending
client
  .table('users')
  .sort('-lastName,firstName,+createdOn')
  .getMany();

// Select specific columns for efficiency
client
  .table('users')
  .select('id', 'firstName', 'lastName', 'email', 'role')
  .getMany();

// Load nested relations
client
  .table('posts')
  .withRelations(['author', 'comments', 'comments.author'])
  .getMany();

// Combining multiple query features
client
  .table('users')
  .filter('isActive=true')
  .sort('-createdOn')
  .paginate(2, 25)
  .select('id', 'firstName', 'lastName', 'email')
  .withRelation('posts')
  .getMany();

The new query builder generates optimized SQL queries with features like:

  • WITH clauses for complex queries
  • LEFT JOIN LATERAL for efficient relation loading
  • Proper quoting of table and column names
  • Automatic handling of pagination with offset/limit
  • Support for complex sorting with multiple columns and directions
  • Proper handling of column name conventions (lowerCamelCase in code, lowercase in database)

Direct Database Access

For more advanced use cases, you can access the DatabaseClient directly:

import { createClient } from 'stark-fetch';

const client = createClient({
  client: 'pg',
  connection: {
    host: 'localhost',
    user: 'postgres',
    password: 'password',
    database: 'myapp'
  }
});

// Get the DatabaseClient instance
const dbClient = client.getDatabaseClient();

// Get a user by ID with options
const user = await dbClient.getOne('users', '123', {
  withRelation: ['posts'],
  sort: 'createdAt',
  filter: 'status = "active"'
});

Recent Changes

Architecture Improvements

  • Centralized Database Logic: All CRUD operations now reside in the DatabaseClient in the core module
  • Simplified Service Layer: DataService now delegates to DatabaseClient for all operations
  • Clear Separation of Concerns: Core handles database operations, services handle business logic
  • Improved Type Safety: Consistent type definitions across all modules
  • Modular Structure: Organized into core, database, adapters, and utils modules

Code Improvements

  • Reduced Duplication: Database logic is now in one place
  • Consistent Error Handling: Standardized error handling across all operations
  • Better Testability: Clear separation makes unit testing easier
  • Simplified Maintenance: Changes to database logic only need to be made in one place
  • Improved Performance: Direct database access without unnecessary abstraction layers

Contributing

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

License

MIT