https://github.com/theogravity/objection-generator
Generates knex migrations and objection.js models in Typescript from a YAML spec
https://github.com/theogravity/objection-generator
generator knex knexjs migrate model objection objectionjs orm schema typescript yaml
Last synced: about 1 year ago
JSON representation
Generates knex migrations and objection.js models in Typescript from a YAML spec
- Host: GitHub
- URL: https://github.com/theogravity/objection-generator
- Owner: theogravity
- License: mit
- Created: 2020-05-09T17:50:26.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-11T05:30:27.000Z (over 3 years ago)
- Last Synced: 2025-03-24T06:45:25.401Z (about 1 year ago)
- Topics: generator, knex, knexjs, migrate, model, objection, objectionjs, orm, schema, typescript, yaml
- Language: TypeScript
- Homepage:
- Size: 396 KB
- Stars: 13
- Watchers: 3
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# objection-generator
[](https://www.npmjs.com/package/objection-generator)
[](https://circleci.com/gh/theogravity/objection-generator)

[](https://standardjs.com)
Generates [`objection.js`](https://github.com/vincit/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`](http://knexjs.org/) migration file based on the YAML file
- [Installation](#installation)
- [Install the CLI utility](#install-the-cli-utility)
- [Install knex + objection (if you do not have it installed)](#install-knex--objection-if-you-do-not-have-it-installed)
- [Usage](#usage)
- [Objection.js model generation](#objectionjs-model-generation)
- [Sample output](#sample-output)
- [Knex configuration](#knex-configuration)
- [Knex migration generation](#knex-migration-generation)
- [Limitations](#limitations)
- [Usage](#usage-1)
- [Sample output](#sample-output-1)
- [Run the migration](#run-the-migration)
- [YAML spec](#yaml-spec)
# 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 `
```bash
objection-generator generate
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
│ ├── .ts
```
Will generate models that look like this:
```typescript
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`](https://vincit.github.io/objection.js/api/objection/#knexsnakecasemappers)
in your `knex` configuration.
```typescript
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 `
```bash
objection-generator knex
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:
```js
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 /migrate.js`
Modify this file to your liking to work with your own database.
# YAML spec
See `sample.yaml` for an example spec.
```yaml
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
```