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

objection-generator

v1.2.7

Published

Generates knex migrations and objection.js models in Typescript from a YAML spec

Downloads

42

Readme

objection-generator

NPM version CircleCI built with typescript JavaScript Style Guide

Generates objection.js models in Typescript from a YAML specification.

  • Generate your initial set of objection.js models from a YAML file
  • Supports $ref for re-using common definitions
  • Can also generate a basic knex migration file based on the YAML file

Installation

Install the CLI utility

$ npm i objection-generator -g

Install knex + objection (if you do not have it installed)

$ npm i knex objection --save

Usage

Objection.js model generation

$ objection-generator generate <specFile> <outDir>

objection-generator generate <specFile> <outDir>

Generates objection.js models from a YAML file

Positionals:
  specFile  The YAML file to use to generate models.                                         [string] [required]
  outDir    The directory to output the models.                                              [string] [required]                                                [string] [required]

Sample output

Using the sample.yaml spec in this project:

$ objection-generator generate sample.yaml /tmp/lib

Will generate the following folder structure:

/tmp/lib/
├── models/
│   ├── BaseModel.ts
│   ├── <model>.ts

Will generate models that look like this:

import { Model } from 'objection'
import path from 'path'

import { BaseModel } from './BaseModel'

export enum PersonGenderEnum {
  MALE = 'Male',
  FEMALE = 'Female',
  OTHER = 'Other'
}
export enum PersonFavFoodEnum {
  PINE_APPLE = 'pine-apple',
  BLUE_BERRY = 'blueBerry',
  CHEESE_PIZZA = 'cheese_pizza'
}

export class PersonModel extends BaseModel {
  id: string
  name: string
  age: number | null
  gender: PersonGenderEnum
  favFood: PersonFavFoodEnum
  username: string
  created: string

  static tableName = 'persons'

  static get jsonSchema () {
    return {
      type: 'object',
      required: ['name', 'username'],
      properties: {
        id: {
          type: 'string'
        },
        name: {
          type: 'string',
          minLength: 1,
          maxLength: 100
        },
        age: {
          type: ['number', 'null']
        },
        gender: {
          type: 'string',
          enum: ['Male', 'Female', 'Other'],
          default: 'Female'
        },
        favFood: {
          type: 'string',
          enum: ['pine-apple', 'blueBerry', 'cheese_pizza']
        },
        username: {
          type: 'string',
          minLength: 1,
          maxLength: 25,
          default: 'default-user'
        },
        created: {
          type: 'string',
          format: 'date-time'
        }
      }
    }
  }

  static get relationMappings () {
    return {
      movies: {
        relation: Model.ManyToManyRelation,
        modelClass: path.join(__dirname, 'MovieModel'),
        join: {
          from: 'persons.id',
          through: {
            from: 'persons_movies.personId',
            to: 'persons_movies.movieId'
          },
          to: 'movies.id'
        }
      },
      reviews: {
        relation: Model.HasManyRelation,
        modelClass: path.join(__dirname, 'ReviewModel'),
        join: {
          from: 'persons.id',
          to: 'review.authorId'
        }
      }
    }
  }
}

Knex configuration

You must use knexSnakeCaseMappers in your knex configuration.

import { knexSnakeCaseMappers } from 'objection'
import knex from 'knex'

const db = knex({
  client: 'postgres',

  connection: {
    host: '127.0.0.1',
    user: 'objection',
    database: 'objection_test'
  },
  // allows usage of camel cased names in the model
  // and snake cased fields in the database
  ...knexSnakeCaseMappers()
})

Knex migration generation

The YAML can also be used to generate a basic migration file. This can be used as a good starting base for building a desired migration.

Limitations

There are many limitations to the generation since there is not an exact mapping between JSON schema types / information in the objection models to an exact database specification.

Some limitations include:

  • No foreign keys are generated (PRs welcomed - make use of the relations please)
  • No through tables are generated

PRs are welcomed for improvements!

Usage

$ objection-generator knex <specFile> <outDir>

objection-generator knex <specFile> <outDir>

Generates a basic knex migration from a YAML file

Positionals:
  specFile  The YAML file to use to generate models.                                         [string] [required]
  outDir    The directory to output the models.                                              [string] [required]                                      [string] [required]

Sample output

Using the sample.yaml spec in this project:

$ objection-generator knex sample.yaml /tmp/lib

Will generate the following folder structure:

/tmp/lib/
├── migrations/
│   └── 000-init.js
└── migrate.js

Example migration output:

async function up (knex) {
  await knex.schema.createTable('persons', table => {
    table.string('id')
    table.string('name', 100).notNullable()
    table.integer('age')
    table.enu('gender', ['Male', 'Female', 'Other']).defaultTo('Female')
    table
      .string('username', 25)
      .notNullable()
      .defaultTo('default-user')
    table.datetime('created')

    table.primary(['id'])

    table.unique(['username'], 'uniq_username')

    table.index(['age', 'name'], 'name_age_index')
  })
  await knex.schema.createTable('movies', table => {
    table.string('id')
    table.string('name', 255).notNullable()

    table.primary(['id'])
  })
  await knex.schema.createTable('reviews', table => {
    table.string('review_id')
    table.string('author_id').notNullable()
    table.string('movie_id').notNullable()
    table.string('content')

    table.primary(['review_id'])
  })
}

async function down (knex) {
  await knex.schema.dropTable('persons')
  await knex.schema.dropTable('movies')
  await knex.schema.dropTable('reviews')
}

module.exports = {
  up,
  down
}

Run the migration

The output includes a sample migration script that uses sqlite3 as the database driver for quick prototyping.

$ npm i sqlite3 --save-dev

$ node <outputDir>/migrate.js

Modify this file to your liking to work with your own database.

YAML spec

See sample.yaml for an example spec.

config:
  model:
    # Adds a prefix to the class names of the generated objection.js models
    classNamePrefix:
    # Adds a postfix to the class names of the generated objection.js models
    classNamePostfix: Model

# Objection models to generate
models:
  # Defines an objection model named Person (actually PersonModel with the postfix)
  Person:
    # database table name
    tableName: persons
    # maps to Model#jsonSchema()
    # https://json-schema.org/understanding-json-schema/reference/type.html
    # https://vincit.github.io/objection.js/guide/models.html#examples
    jsonSchema:
      required: ['name', 'username']
      properties:
        id:
          type: string
        name:
          type: string
          minLength: 1
          maxLength: 100
        age:
          # You can define a re-usable set of properties and reference them via $ref
          $ref: '#/components/fieldProperties/age'
        gender:
          type: string
          enum: ['Male', 'Female', 'Other']
          default: 'Female'
        favFood:
          type: string
          enum: ['pine-apple', 'blueBerry', 'cheese_pizza']
        childrenCount:
          type: number
          default: 0
        username:
          allOf:
            # combine a ref and a non-ref, see json schema spec for more info
            - $ref: '#/components/fieldProperties/username'
            - default: 'default-user'
        someOtherField:
          type: string
        created:
          type: string
          format: date-time
    # Define relations - maps to Model#relationMappings()
    # https://vincit.github.io/objection.js/guide/relations.html#examples
    relations:
      movies:
        relation: Model.ManyToManyRelation
        modelClass: Movie
        join:
          from: persons.id
          through:
            from: persons_movies.personId
            to: persons_movies.movieId
          to: movies.id
      reviews:
        relation: Model.HasManyRelation
        modelClass: Review
        join:
          from: persons.id
          to: review.authorId
    # Section for knex-specific generation
    database:
      # define unique indices
      unique:
        # made-up name for the unique index
        uniq_username:
          # columns to add to unique index
          # values will always be converted to snake case
          columns: ['username']
      # Define indices
      index:
        # made-up name for the index
        name_age_index:
          # columns to index
          # values will always be converted to snake case
          columns: ['age', 'name']
      exclude:
        # exclude these fields from being generated in the migration file
        # this is if you want to have a field defined in the model
        # but not in the database
        columns: ['someOtherField']
  Movie:
    tableName: movies
    jsonSchema:
      required: ['name']
      properties:
        id:
          type: string
        name:
          type: string
          minLength: 1
          maxLength: 255
    relations:
      reviews:
        relation: Model.HasManyRelation
        modelClass: Review
        join:
          from: movie.id
          to: review.movieId
  Review:
    tableName: reviews
    # If you want to use a primary key that's not called "id"
    idColumn: reviewId
    jsonSchema:
      required: ['authorId', 'movieId']
      properties:
        reviewId:
          type: string
        authorId:
          type: string
        movieId:
          type: string
        content:
          type: string
    relations:
      author:
        relation: Model.HasOneRelation
        modelClass: Person
        join:
          from: reviews.authorId
          to: persons.id

# components are re-usable elements that can be
# referenced in the model via $ref
components:
  # This is a made up section used for
  # defining common field properties
  fieldProperties:
    age:
      type: ['number', 'null']
    username:
      type: string
      minLength: 1
      maxLength: 25