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

driftql-react

v1.7.2

Published

DriftQL is a powerful Elegant ORM client designed to simplify data fetching and state management. It provides an intuitive API for making queries and mutations, while also offering features like caching, error handling, and real-time updates. With DriftQL

Readme

DriftQL React 🚀

The bridge between your Tonka Framework application and React. Seamlessly expose your Elegant ORM models to the frontend with a powerful, secure, and declarative API.

🌟 Features

  • 🪄 Auto-Data Binding: Inject ORM data directly into DOM attributes (e.g., auto-fill an img src).
  • ⚛️ Flexible Usage: Supports both simple attribute injection and advanced Render Props with loading/error states.
  • 🔒 Secure by Design: Built-in support for API keys, model whitelisting, and strict SQL sanitization via your DriftQL backend.
  • 🎯 Fluent Querying: Use where, orderBy, and limit directly in your components, just like in your backend code.
  • 🛠️ Lifecycle Hooks: Handle onLoadStart and onComplete for fine-grained control over the loading state.

📦 Installation

npm install driftql-react
# or
yarn add driftql-react

Backend installation:

composer require tonka/driftql

⚙️ Configuration

Auto-Configuration (Recommended)

The Tonka Framework provides a command to generate both the frontend and backend configuration files automatically for you.

Run the following command in your terminal:

php tonka driftql:config

This command will create/overwrite:

  1. drift.config.js (Frontend configuration)
  2. config/driftql.php (Backend configuration)

Once generated, you can review and customize the files below.


1. Frontend Configuration

The generated drift.config.js file will look like this:

// drift.config.js
export default {
  // The base URL of your DriftQL backend bridge
  baseURL: '/api/bridge',
  
  // Request timeout in milliseconds
  timeout: 5000,
  
  // Cache strategy (RequestCache)
  cache: 'default', 
  
  // The public key required to authenticate requests with the backend
  bridge_public_key: 'd3597f10e13310490809c832b898445e88face0bb7635b846d1b651f62417a29'
};

Then, initialize the package in your entry file (e.g., main.tsx or App.js):

import { Client } from 'driftql-react';
import config from './drift.config';

Client.init(config);

2. Backend Configuration (Tonka / PHP)

The generated config/driftql.php file manages security, whitelisting, and data access policies.

// config/driftql.php
<?php

return [
    /*
    |--------------------------------------------------------------------------
    | DRIFTQL BRIDGE ENABLED
    |--------------------------------------------------------------------------
    */
    'enabled' => env('DRIFTQL_BRIDGE_ENABLED', true),

    /*
    |--------------------------------------------------------------------------
    | DRIFTQL BRIDGE KEY
    |--------------------------------------------------------------------------
    | A secret key that the front-end must provide to access the DriftQL bridge.
    | This is an additional layer of security to prevent unauthorized access.
    */
    'bridge_public_key' => env('DRIFTQL_BRIDGE_KEY', 'your-public-key-here'),

    /*
    |--------------------------------------------------------------------------
    | MODEL WHITELIST
    |--------------------------------------------------------------------------
    | List of models (classes) that the front-end is allowed to query. 
    | If a model is not in this list, the query is rejected immediately.
    */
    'whitelist' => [
        'allowed_models' => [
            \App\Models\User::class,
            \App\Models\Employee::class,
            // Note : \App\Models\Admin is not here, so it is inaccessible via this route
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | AUTHORIZATION (Policies & Row Level Security)
    |--------------------------------------------------------------------------
    | Defines access restrictions by Role and by Model. 
    | The backend automatically applies these "Global Scopes".
    |
    | Format :
    | 'ModelName' => [
    |      'RoleName' => [ Filtering rule ]
    | ]
    |
    | Note: You can specify a class implementing \Tonka\DriftQL\Security\Contract
    | instead of an array. This class exposes the authorize() method.
    */
    'policies' => [
        \App\Models\User::class => [
            // Admins see everything (no filter)
            'admin' => null, 
            
            // Editors cannot see other admins
            'editor' => [
                'column' => 'role',
                'operator' => '!=',
                'value' => 'admin', 
            ],
            
            // Standard users only see themselves
            'user' => [
                'column' => 'id',
                'operator' => '=',
                'value' => 'current_user_id',
            ],
        ],
        
        // Example using a Policy Class
        \App\Models\Post::class => \App\Contracts\PostContract::class,
    ],

    /*
    |--------------------------------------------------------------------------
    | HARDCODED LIMITATION (DoS Protection)
    |--------------------------------------------------------------------------
    | Enforce ceilings to protect database performance.
    */
    'limits' => [
        // Maximum number of records the front end can request at once
        'max_limit' => 100, 
        
        // Default number if the front does not specify a limit
        'default_limit' => 20,
        
        // Maximum number allowed for the offset (to avoid excessively deep pagination)
        'max_offset' => 10000,
    ],

    /*
    |--------------------------------------------------------------------------
    | SQL SANITIZATION
    |--------------------------------------------------------------------------
    | Security guidelines for constructing the request.
    */
    'security' => [
        // The use of "raw SQL" from the front end is strictly prohibited.
        // Only structured "where" clauses (column, operator, binding) are accepted.
        'allow_raw_sql' => false,

        // Checks that the columns requested in 'orderBy' or 'where' actually exist
        // in the database schema.
        'strict_column_check' => true,
    ],
];

🚀 Usage Examples

1. Basic Configuration

// App.tsx
import { ElegantProvider } from 'driftql-react';

function App() {
  return (
    <ElegantProvider config={{ 
      timeout: 10000,
      cache: true
    }}>
      <YourApp />
    </ElegantProvider>
  );
}

2. Using useElegant Hook

// shared/UserList.tsx
import { useElegant } from 'driftql-react';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserList() {
  const { all, find, delete: deleteUser } = useElegant<User>('User');
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    all()
      .then(setUsers)
      .finally(() => setLoading(false));
  }, []);

  const handleDelete = async (id: number) => {
    await deleteUser(id);
    setUsers(users.filter(u => u.id !== id));
  };

  if (loading) return <div>Loading...</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.name} - {user.email}
          <button onClick={() => handleDelete(user.id)}>Delete</button>
        </li>
      ))}
    </ul>
  );
}

3. Using useQueryBuilder Hook

// shared/UserSearch.tsx
import { useQueryBuilder } from 'driftql-react';

function UserSearch() {
  const { getBuilder } = useQueryBuilder<User>('User');
  const [results, setResults] = useState<User[]>([]);

  const search = async (term: string) => {
    const builder = getBuilder();
    const data = await builder
      .where({ column: 'name', operator: 'LIKE', value: `%${term}%` })
      .where({ column: 'active', operator: '=', value: true })
      .orderBy('name', 'ASC')
      .get();
    setResults(data);
  };

  return (
    <div>
      <input onChange={(e) => search(e.target.value)} placeholder="Search..." />
      <ul>
        {results.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

4. Define a Model

// models/User.ts
import { Elegant } from 'driftql-react';

export interface UserData {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'moderator';
  status: 'active' | 'inactive' | 'pending';
  created_at: string;
  updated_at: string;
}

export class User extends Elegant implements UserData {
  static resourceName = 'User';
  
  id!: number;
  name!: string;
  email!: string;
  role!: 'admin' | 'user' | 'moderator';
  status!: 'active' | 'inactive' | 'pending';
  created_at!: string;
  updated_at!: string;

  // Méthodes personnalisées
  get isAdmin(): boolean {
    return this.role === 'admin';
  }

  get isActive(): boolean {
    return this.status === 'active';
  }

  get displayName(): string {
    return `${this.name} (${this.email})`;
  }
}

5. CRUD

// CREATE
const newUser = await User.store({
  name: 'John Doe',
  email: '[email protected]',
  role: 'user',
  status: 'active'
});

// READ
const user = await User.find(123);
const allUsers = await User.all();
const activeUsers = await User
  .where({ column: 'status', operator: '=', value: 'active' })
  .get();

// UPDATE
const updated = await User.update(123, {
  name: 'Jane Doe',
  role: 'admin'
});

// DELETE
await User.delete(123);

// SAVE
const user = new User();
user.name = 'John';
user.email = '[email protected]';
await user.save();

// REFRESH
const user = await User.find(123);
await user.refresh();

// EXISTS
if (await user.exists()) {
  console.log('User exists');
}

6. Advance Request

// Pagination
const result = await User
  .where({ column: 'status', operator: '=', value: 'active' })
  .paginate(1, 15);

console.log(result.data); // Users
console.log(result.pagination.total); // Total

// Eager Loading
const users = await User
  .with(['posts', 'profile'])
  .where({ column: 'status', operator: '=', value: 'active' })
  .get();

// Join
const posts = await Post
  .join({
    resource: 'User',
    type: 'inner',
    fkey: 'user_id',
    okey: 'id'
  })
  .where({ column: 'users.role', operator: '=', value: 'admin' })
  .get();

// Agregation
const stats = await User
  .groupBy('role')
  .select('role, COUNT(*) as total')
  .get();

// Find Or Fail
try {
  const user = await User.findOrFail(123);
} catch (error) {
  console.error('User not found');
}

// Store Or Fail
try {
  const user = await User.storeOrFail({ name: 'John' });
} catch (error) {
  console.error('Creation failed');
}

📚 API Reference

| Category | Method | Description | | :--- | :--- | :--- | | CRUD | find() | Find by ID | | | findOrFail() | Find or error | | | all() | All records | | | store() | Create new record | | | storeOrFail() | Create or error | | | update() | Update a record | | | updateOrFail() | Update or error | | | delete() | Destroy a record | | | deleteOrFail() | Destroy or error | | Instance | with() | Eager-loading | | | where() | Condition WHERE | | | having() | Condition HAVING | | | savg() | Save (create/update) | | | saveOrFail() | Save or error | | | refresh() | Refresh | | | exists() | Verify if exists | | | toJSON() | Convert to JSON | | | toFormData() | Convert to FormData |

🔐 Security Note

DriftQL is designed to expose your ORM securely. Ensure your backend whitelist and policies are correctly configured to prevent unauthorized data access. The bridge_public_key ensures that only your frontend application can communicate with the bridge.

📝 License

MIT