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

@danielgl/punk-orm

v1.0.8

Published

An Entity Framework-inspired ORM for Bun supporting SQLite, Postgres, MySQL and MSSQL.

Downloads

768

Readme

🎸 PunkORM

An Entity Framework-inspired ORM for Bun — supporting SQLite, PostgreSQL, MySQL/MariaDB, and MSSQL with decorators, DataContext, DbSet<T> and a fluent LINQ-like query builder.


Installation

bun add @danielgl/punk-orm reflect-metadata
# Install your driver:
# bun add postgres      # for PostgreSQL
# bun add mysql2        # for MySQL/MariaDB
# bun add mssql         # for MSSQL

Add to your tsconfig.json:

{
    "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}

🗄️ Supported Databases

PunkORM supports multiple database engines. Choose the appropriate adapter for your project:

SQLite

Uses Bun's native bun:sqlite driver. No external dependencies required.

import { BunSQLiteAdapter } from "@danielgl/punk-orm";
const adapter = new BunSQLiteAdapter("./app.db");

PostgreSQL

Requires postgres (postgres.js).

import { PostgresAdapter } from "@danielgl/punk-orm";
// URL Format: postgres://user:password@host:port/database
const adapter = new PostgresAdapter("postgres://postgres:pass@localhost:5432/mydb");

MySQL / MariaDB

Requires mysql2.

import { MySqlAdapter } from "@danielgl/punk-orm";
// URL Format: mysql://user:password@host:port/database
const adapter = new MySqlAdapter("mysql://root:pass@localhost:3306/mydb");

MSSQL (SQL Server)

Requires mssql (tedious). Supports both URL and traditional connection strings.

import { MsSqlAdapter } from "@danielgl/punk-orm";

// 1. URL Format (Recommended)
// mssql://user:password@host:port/database
const adapter1 = new MsSqlAdapter("mssql://sa:password@localhost/master");

// 2. Traditional Connection String
const adapter2 = new MsSqlAdapter(
    "Server=localhost;Database=master;User Id=sa;Password=password;TrustServerCertificate=True;"
);

[!TIP] Special Characters: Connection strings now robustly handle passwords containing @, #, or other special characters without needing manual URL encoding.


Quick Start

1. Define Entities

import "reflect-metadata";
import { Entity, Column, PrimaryGeneratedColumn, OneToMany, ManyToOne } from "@danielgl/punk-orm";

@Entity("users")
class User {
    @PrimaryGeneratedColumn("uuid")
    public id: string;

    @Column({ type: "text" })
    public name: string;

    @Column({ type: "text", unique: true })
    public email: string;

    @OneToMany(() => Post, { foreignKey: "authorId" })
    public posts: Post[];
}

@Entity("posts")
class Post {
    @PrimaryGeneratedColumn("uuid")
    public id: string;

    @Column({ type: "text" })
    public title: string;

    @Column({ type: "integer", default: 0 })
    public views: number;

    @Column({ type: "text" })
    public authorId: string;

    @ManyToOne(() => User)
    public author: User;
}

2. Create a DataContext

import { DataContext, BunSQLiteAdapter, PostgresAdapter } from "@danielgl/punk-orm";

class AppContext extends DataContext {
    users = this.set(User);
    posts = this.set(Post);

    // Optional: Seed data or configure models
    protected onModelCreating(): void {
        console.log("Configuring models...");
    }
}

// Use SQLite
const ctx = new AppContext(new BunSQLiteAdapter("./app.db"), {
    logging: ["query", "error"],
    autoMigrations: true
});

// Or PostgreSQL
// const ctx = new AppContext(new PostgresAdapter("postgres://user:pass@localhost:5432/db"));

await ctx.initialize();

3. CRUD via DbSet

// INSERT
const user = ctx.users.create({ name: "Alice", email: "[email protected]" });
ctx.users.add(user);
await ctx.users.saveChanges();

// SELECT
const all = await ctx.users.find();
const alice = await ctx.users.findOneOrFail({ email: "[email protected]" });

// UPDATE
alice.name = "Alice Smith";
ctx.users.update(alice);
await ctx.users.saveChanges();

// DELETE
ctx.users.remove(alice);
await ctx.users.saveChanges();

4. Fluent Query Builder (LINQ-inspired)

// Eager Loading (Joins)
const postsWithAuthor = await ctx.posts
    .include("author")
    .where((p) => p.views.gt(100))
    .toList();

// Complex filtering with projections
const topPosts = await ctx.posts
    .asQuery()
    .where((p) => p.views.gt(100).and(p.title.contains("Punk")))
    .orderByDescending((p) => p.views)
    .select((p) => ({ id: p.id, title: p.title })) // Optimized projection
    .take(10)
    .toList();

// Or raw SQL style
const raw = await ctx.posts
    .createQueryBuilder()
    .where("views > ?", [100])
    .orderBy("views", "DESC")
    .getMany();

🛠️ CLI (Punk CLI)

PunkORM comes with a CLI to handle migrations and scaffolding.

# Initialize project (scaffolds punk.config.ts)
bun punk init

# Generate a migration based on entity changes
bun punk generate migration --name create_users

# Run all pending migrations
bun punk run migration

# Check status
bun punk status

API Reference

Decorators

| Decorator | Description | | ------------------------------------------------ | ----------------------------------------------------------------------------- | | @Entity(name?) | Maps a class to a SQL table | | @Column(opts?) | Maps a property to a column. Options: type, nullable, unique, default | | @PrimaryKey() | Manual primary key (no auto-generation) | | @PrimaryGeneratedColumn("uuid" \| "increment") | Auto-generated PK | | @OneToMany(() => Entity, { foreignKey }) | 1-to-N relation | | @ManyToOne(() => Entity, opts?) | N-to-1 relation (owns FK) |

Column Types

text · integer · real · blob · boolean · datetime · uuid

DataContext

| Method | Description | | ------------------- | -------------------------------------------- | | initialize() | Creates/migrates tables | | onModelCreating() | Overridable method for seeding/entity config | | set(Entity) | Returns DbSet<T> for the entity | | adapter | Raw database adapter | | close() | Closes the connection |

DbSet<T>

| Method | Description | | ---------------------- | --------------------------------------------- | | find(opts?) | SELECT with optional where/orderBy/skip/take | | findOne(where) | Returns first match or null | | findOneOrFail(where) | Returns first match or throws | | create(data) | Instantiates entity (not persisted) | | add(entity) | Stages INSERT | | update(entity) | Stages UPDATE | | remove(entity) | Stages DELETE | | bulkInsert(entities) | Immediate high-performance INSERT | | bulkUpdate(entities) | Immediate high-performance UPDATE (CASE) | | bulkDelete(entities) | Immediate high-performance DELETE (IN) | | saveChanges() | Flushes all staged changes in a transaction | | include(relation) | Joins a relation (Eager Loading) | | asQuery() | Starts a fluent query chain | | createQueryBuilder() | Returns a QueryBuilder scoped to this table |


Development

bun run dev    # run demo
bun test       # run test suite
bun run build  # build to dist/