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 🙏

© 2024 – Pkg Stats / Ryan Hefner

tiny-orm

v0.1.2

Published

A super tiny ORM for TypeScript.

Downloads

7

Readme

tiny-orm

A super tiny ORM for TypeScript.

tiny-orm lets you

  • auto-validate TypeScript classes using Joi.
  • access snake_cased DB objects through camelCased JavaScript objects.
  • compose simple classes together to model more complex structures.

Installation

$ npm install tiny-orm

Usage

Basic

At its simplest, any class properties can be decorated with a Joi schema like so:

class User extends TinyORM<{}> {
    @prop(Joi.number().min(1)) // prop decorator for valid id
    id: number;

    @prop(Joi.string().email()) // prop decorator for valid email
    email: string;

    @prop() // no runtime validation specified, only static string validation will apply
    name: string;

    constructor(id: number, email: string, name: string) {
        super();

        this.id = id;
        this.email = email;
        this.name = name;
    }
}

const user = new User(1, '[email protected]', 'John Doe');
user.validate(); // ok

user.id = 0;
user.validate(); // throws `User.id must be larger than or equal to 1`

The same example can be enhanced to auto-validate upon each mutation:

@strict // validate() will be automatically called
class User extends TinyORM<{}> {
    @prop(Joi.string().email())
    email: string;

    constructor(email: string) {
        super();
        this.email = email;
    }
}

const user = new User('[email protected]'); // validates without errors

user.email = 'abc' ; // throws `User.email must be a valid email`

Advanced

Better static typing

Get stronger static typing by supplying an interface:

interface IUser {
    id: number;
    email: string;
}

@strict
class User extends TinyORM<IUser> implements IUser {
    @prop(Joi.number().min(1))
    id: number;

    @prop(Joi.string().email())
    email: string;

    // constructor no longer necessary
}

// props to constructor are available through the IUser interface for auto-completion
const user = new User({
    id: 1,
    email: '[email protected]'
});

getInstance

Normalise data from an arbitrary_source (such as a database) to an interface with camelCased properties:

interface IUser {
    userId: number;
    userEmail: string;
}

@strict
class User extends TinyORM<IUser> implements IUser {
    @prop(Joi.number().min(1))
    userId: number;

    @prop(Joi.string().email())
    userEmail: string;
}

const pgData = {
    user_id: 1,
    user_email: '[email protected]'
}

const user = User.getInstance(pgData) as User; // init object by calling the static getInstance method

user.userId === pgData.user_id;
user.userEmail === pgData.user_email;

toObject, toDbObject and toString

Get a validated object structure just with all the @props:

interface IUser {
    userId: number;
    userEmail: string;
}

@strict
class User extends TinyORM<IUser> implements IUser {
    @prop(Joi.number().min(1))
    userId: number;

    @prop(Joi.string().email())
    userEmail: string;

    someUtilityFunction() {
        return this.userId + ': ' + this.userEmail;
    }
}

const user = new User({
    userId: 1,
    userEmail: '[email protected]'
});

user.someUtilityFunction(); // 1: [email protected]

user.toObject(); // { userId: 1, userEmail: '[email protected]' }
user.toDbObject(); // { user_id: 1, user_email: '[email protected]' }
user.toString(); // overridden method to get JSON representation of .toObject()

Composition

This is left to your imagination. Please note, however, that it's not possible to validate nested objects if they are not directly created using TinyORM. This applies especially to nested objects created via the getInstance method.

// Define interfaces

interface IPost {
    id: string;
    title: string;
    createdAt: string;
    comments?: IComment[];
}

interface IComment {
    id: string;
    body: string;
    children?: IComment[];
}

// Declare classes with validation code

@strict
class Post extends TinyORM<IPost> implements IPost {
    @prop(Joi.string().guid())
    id: string;

    @prop()
    title: string;

    @prop(Joi.date().iso())
    createdAt: string;

    @prop(Joi.array())
    comments?: IComment[];
}

@strict
class Comment extends TinyORM<IComment> implements IComment {
    @prop(Joi.string().guid())
    id: string;

    @prop(Joi.string().min(5))
    body: string;

    @prop(Joi.array())
    children?: IComment[];
}

// Create an object using the Post and Comment constructors. These will be fully validated.

let post = new Post({
    id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148f',
    title: 'The blog post',
    createdAt: new Date().toISOString(),
    comments: [
        new Comment({
            id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148b',
            body: 'First!',
            children: [
                new Comment({
                    id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148d',
                    body: 'Second!'
                })
            ]
        })
    ]
});

// Or create the object using statically typed object notation.
// Anything below Post will not be validated by default.

post = new Post({
    id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148f',
    title: 'The blog post',
    createdAt: new Date().toISOString(),
    comments: [
        {
            id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148b',
            body: 'First!',
            children: [
                {
                    id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148d',
                    body: 'Second!'
                }
            ]
        }
    ]
});

// Or use getInstance. Only Post will be validated.

post = Post.getInstance({
    id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148f',
    title: 'The blog post',
    created_at: new Date().toISOString(),
    comments: [
        {
            id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148b',
            body: 'First!',
            children: [
                {
                    id: '3ed44ac2-4dd8-4a2a-9aaa-879e4a44148d',
                    body: 'Second!'
                }
            ]
        }
    ]
}) as Post;

// In all these cases (assuming strictNullChecks is temporarily disabled :)):

post.toObject().comments[0].children[0].body === 'Second!';