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 🙏

© 2025 – Pkg Stats / Ryan Hefner

neopg

v2.0.9

Published

orm for postgres

Readme

NeoPG

The Next Generation PostgreSQL ORM for Node.js

NeoPG is a high-performance, zero-dependency ORM built directly on top of postgres.js — the fastest PostgreSQL client for Node.js.

It bridges the gap between the developer experience (DX) of a chainable Query Builder and the raw performance of native SQL Template Literals.

🪭 中文文档 ☯️

🚀 Key Features

  • Powered by postgres.js: Inherits the incredible speed and stability of the fastest PG client.
  • Zero Dependencies: The core driver is vendored and optimized internally. No heavy dependency tree.
  • Hybrid API: Enjoy the ease of Chainable/Fluent APIs (like .where().select()) combined with the power of Tagged Template Literals.
  • Performance First: Zero string concatenation logic. All queries are compiled into efficient fragments and executed natively.
  • Auto Schema Sync: define your models in code, and NeoPG syncs the table structure, indices, and foreign keys automatically.
  • Type Smart: Automatic type casting for aggregations (sum, avg returns numbers, not strings) and JSON handling.

📦 Installation

npm install neopg

🔌 Initialization

Connect to Database

const NeoPG = require('neopg');

const config = {
  host: 'localhost',
  port: 5432,
  database: 'my_db',
  user: 'postgres',
  password: 'password',
  max: 10,             // Connection pool size
  idle_timeout: 30,    // Idle connection timeout in seconds
  debug: false,        // Enable query logging
  schema: 'public'     // Default schema
};

const db = new NeoPG(config);

Close Connection

await db.close()

📝 Defining a Model

Create a model file (e.g., models/User.js). Your class should extend NeoPG.ModelChain.

const { ModelChain, dataTypes } = require('neopg')

class User extends ModelChain {
  static schema = {
    tableName: 'users',
    modelName: 'User', // Optional, defaults to tableName
    primaryKey: 'id',
    
    // Auto-sync table structure based on this definition
    column: {
      id: { 
        type: dataTypes.ID, // Auto-generates Snowflake-like ID 
      },
      username: { 
        type: dataTypes.STRING(100), 
        required: true 
      },
      email: { 
        type: dataTypes.STRING(255), 
        required: true 
      },
      age: { 
        type: dataTypes.INT, 
        default: 18 
      },
      meta: { 
        type: dataTypes.JSONB 
      },
      created_at: {
        type: dataTypes.BIGINT,
        timestamp: 'insert' // Auto-fill on insert
      },
      updated_at: {
        type: dataTypes.BIGINT,
        timestamp: 'update' // Auto-fill on insert & update
      }
    },

    // Indexes
    index: ['email', 'age'],
    unique: ['username']
  }
}

module.exports = User

🛠 CLI Model Generator

NeoPG includes a built-in CLI tool to quickly generate model files with boilerplate code.

Usage

Run via npx (no global installation required):

npx neopg-model [options] [model_names...]

Options

  • --dir=<path>: Specify the output directory (default: ./model).

Examples

1. Basic Generation

npx neopg-model user
# Creates: ./model/user.js
# Class: User
# Table: user

2. Naming Convention (Hyphenated) NeoPG automatically converts hyphenated names to CamelCase for the class and snake_case for the table.

npx neopg-model user-log
# Creates: ./model/user-log.js
# Class: UserLog
# Table: user_log

3. Multiple Models & Custom Directory

npx neopg-model --dir=./src/models product order-item
# Creates:
#   ./src/models/product.js
#   ./src/models/order-item.js

4. ES Modules (.mjs) If you suffix the name with .mjs, it generates ESM syntax (export default).

npx neopg-model config.mjs

⚙️ Registration & Sync

Initialize NeoPG and register your models. You can define models using classes or configuration objects.

Registering Models

NeoPG provides three methods for registration:

  • define(model): The standard method. Throws an error if a model with the same name already exists.
  • add(model): Alias for define.
  • set(model): Overwrites the existing model if the name conflicts. Useful for hot-reloading or dynamic schema updates.
const User = require('./models/User')

// 1. Standard Registration (Safe)
// Will throw error: "[NeoPG] modelName conflict: User" if registered twice
db.define(User)

// 2. Force Overwrite (Reset)
// Updates the definition for 'User' even if it exists
db.set(User)

// 3. Register using a plain object (Quick prototype)
db.define({
  tableName: 'logs',
  column: {
    message: 'string',
    level: 'int'
  }
})

console.log(db.has('User'))

Syncing Database

Sync the table structure to the database based on registered models.

// Sync Table Structure (DDL)
// options: { force: true } will drop columns not defined in schema
await db.sync({ force: false })

console.log('Database synced!')

📂 Auto-loading Models

Instead of manually importing and defining each model, you can load all models from a directory.

Rules:

  • Only .js and .mjs files are loaded.
  • Files starting with _ are ignored (useful for utils/helpers).
  • Files starting with ! are ignored (useful for disabled models).
const db = new NeoPG(config)

// Load all models from the './models' directory
// This is asynchronous because it supports .mjs dynamic imports
await db.loadModels('./models')

//load files
await db.loadFiles(['./models2/WxUser.js', './models2/Role.js'])

// Now you can sync and use them
await db.sync()

🔍 Querying

NeoPG provides a fluent, chainable API that feels natural to use.

Basic Find

// Get all users
const users = await db.model('User').find();

// Select specific columns
const users = await db.model('User')
  .select('id, username')
  .limit(10)
  .find();

// Get a single record
const user = await db.model('User').where({ id: '123' }).get();

// Pagination
const page2 = await db.model('User').page(2, 20).find(); // Page 2, Size 20

Chained Where

await db.model('User')
  .where({ 
    age: 18, 
    status: 'active' 
  })
  .where('create_time', '>', 1600000000)
  .where('id IS NOT NULL')
  .find()

Complex Where with Template Literals

This is where NeoPG shines. You can mix raw SQL fragments safely using the sql tag from the context.

// db.sql is the native postgres instance
const { sql } = db; 

await db.model('User')
  .where({ status: 'active' })
  // Safe parameter injection via Template Literals
  .where(sql`age > ${20} AND email LIKE ${'%@gmail.com'}`)
  .find();

📊 Aggregation

NeoPG handles type casting automatically (e.g., converting PostgreSQL count string results to Javascript Numbers).

// Count
const total = await db.model('User').where({ age: 18 }).count();

// Max / Min
const maxAge = await db.model('User').max('age');

// Sum / Avg (Returns Number, not String)
const totalScore = await db.model('User').sum('score');
const avgScore = await db.model('User').avg('score');

// Group By
const stats = await db.model('User')
  .select('city, count(*) as num')
  .group('city')
  .find();

✏️ Write Operations

Insert

// Insert one
const newUser = await db.model('User').insert({
  username: 'neo',
  email: '[email protected]'
})
// ID and Timestamps are automatically generated if configured in Schema

// Insert multiple (Batch)
await db.model('User').insert([
  { username: 'a' }, 
  { username: 'b' }
])

Update

const updated = await db.model('User')
  .where({ id: '123' })
  .update({
    age: 99,
    meta: { role: 'admin' }
  });

Delete

await db.model('User')
  .where('age', '<', 10)
  .delete();

Returning Data

By default, write operations might not return data depending on the driver optimization. You can enforce it:

const deletedUsers = await db.model('User')
  .where('status', 'banned')
  .returning('id, username') // or returning('*')
  .delete();

⚡ Raw SQL (Template Literals)

NeoPG exposes the full power of postgres.js. You don't need the ModelChain for everything.

📚 Reference: Full documentation for the SQL tag can be found at the postgres.js GitHub page.

// Access the native driver
const sql = db.sql;

// Execute raw SQL safely
const users = await sql`
  SELECT * FROM users 
  WHERE age > ${20}
`;

// Dynamic tables/columns using helper
const table = 'users';
const column = 'age';
const result = await sql`
  SELECT ${sql(column)} 
  FROM ${sql(table)}
`;

🤝 Transactions

NeoPG provides a unified transaction API. It supports nested transactions (Savepoints) automatically.

Using NeoPG Context (Recommended)

// Start a transaction scope
const result = await db.transaction(async (tx) => {
  // 'tx' is a TransactionScope that mimics 'db'
  
  // 1. Write operation
  const user = await tx.model('User').insert({ username: 'alice' });
  
  // 2. Read operation within transaction
  const count = await tx.model('User').count()
  
  // 3. Throwing an error will automatically ROLLBACK
  if (count > 100) {
    throw new Error('Limit reached')
  }
  
  return user
})

Using Raw Postgres Transaction

await db.sql.begin(async (sql) => {
  // sql is the transaction connection
  await sql`INSERT INTO users (name) VALUES ('bob')`;
})

License

ISC