Skip to content

elder-orm/core

Repository files navigation

Elder

An ORM for javascript and typescript

npm version Build Status Greenkeeper badge semantic-release Commitizen friendly styled with prettier Code Climate

Quickstart

Install

Inside your project, run one of the following commands to install Elder

  • via Yarn: yarn add elder-core
  • or via npm: npm install elder-core

Setup

While you could write all your ORM code in a single file if you wanted, we recommend breaking up your code in the following way.

Folder structure

We recommend creating the following directory structure when using Elder. This is in part because once a cli tool is developed, it will most likely follow the conventions laid out in this guide.

/my-project
  /orm
    /models
    /types
    config.ts
    index.ts

The models folder

The orm/models directory is where you should put your model files. Models are the most important part of Elder and each model represents a table in a database. When naming your files we recommend 1 file per database table named in singular form, lower case and with dashes used to separate any words.

Examples

  • models/post.ts
  • models/post-comment.ts

The types folder

Types are a way to tell the elder how to map you model's properties to columns in the database. Elder comes with some base types which can be used directly or as a starting point for building more complex types.

The types Elder ships with are:

  • string
  • boolean
  • date
  • number

Types are very flexible and can include validations. You are encouraged to build various types to meet your applications needs. See the documentation on types for more information.

Types should be placed in separate files inside the /types folder. Naming should follow a singularized, lowercase, dash separated convention.

Examples:

  • /types/age.ts
  • /types/cat-age.ts

The config file

The config file is used, among other things, to store details about the database connection. For now we just need to create the file /orm/config.ts and populate it with the following information. (Changing values as appropriate).

Example

export default {
  adapters: {
    default: {
      host: '127.0.0.1',
      user: 'user',
      pass: 'sshhh',
      port: 5432
    }
  }
}

The property adapters with a nested property default tells Elder to use these connection details whenever the default database adapter is used. It's possible to define additional adapters, more about that in the adapters section.

The index file

In the index file we need to tie everything we have laid out so far together.

First we import the Elder module from elder-core, then our config file.

Next we import each type we have defined in our type folder (none so far, we'll get to that) and each model file we have defined in our model folder. (Coming up in the next section).

Finally, we create and export a new instance of Elder passing it all the things we have imported.

Example:

import Elder from 'elder-core'
import config from './config'

/*
  import CatAgeType from './types/cat-age'
  import DogSizeType from './types/doge-size'
*/

/*
  import Cat from './models/cat'
  import Dog from './models/dog'
  etc
*/

export default Elder.create({
  config,
  types: {
    /*
      give any custom types we have defined and imported here:
      'cat-age': CatAgeType,
      'dog-size': DogSizeType
    */
  },
  models: {
    /*
      give any models we have defined and imported here:
      'cat': Cat,
      'dog': Dog
    */
  }
})

Worth noting also is that there are several other concepts working behind the scenes. Both adapters and serializers are always present but Elder will give you the defaults if you omit them from setup. You can read more about these 2 concepts in their respective sections of the guide.

Defining models

With project structure in hand we can now start creating models to use to interface with our database.

Each model should be in it's own file in the orm/models directory. If we were to create a Cat model, the file should be orm/models/cat.ts.

Model files should export a single model and extend either the base Model class or another model class.

Example

import { Model } from 'elder-core'

export default Cat extends Model {}

We define mappings between properties on our model and columns in the database using the @type decorator. For now, we will just use the built in basic types string, number, date and boolean as we haven't yet defined any application specific types (which we will do in the next section)

Example

import { Model, type } from 'elder-core'

export default Cat extends Model {
  @type('string') name
}

We have told Elder that our cat has a corresponding name column in the cat table and that it is string based (which in the case of postgres is varchar).

Defining types

When defining models we saw that we need to map model properties to database table columns and to do that we use the @type decorator. One of the key features of Elder is that is possible and even encouraged to develop different mappings to suit your needs. This is great because you can easily add support for database specific features eg. geospatial and you can add validations to ensure data integrity.

The best way to define a type is to extend an existing type. We can choose the type the most closely resembles our needs, override whichever methods make sense and go from there.

By convention, type classes should be placed in the orm/types folder and be named singularly, lowercase with dashes instead of spaces.

Heres an example of creating a type that represents the age of a cat. It adds validation to make sure the value is between 0 and 30 (Or can cats get older than that??)

Example

// orm/types/cat-age.ts
import { NumberType } from 'elder-core'

export default CatAgeType extends NumberType {
  validate (value: number): boolean {
    return value >= 0 && value <= 30
  }
}

And now back in our model file, we can reference this new type using our @type decorator like so:

Example

// orm/models/cat.ts
export default Cat extends Model {
  @type('string') name
  @type('cat-age') age
}

Finally we need to go back to our orm/index.ts file and load in our cat model and cat-age type.

Example Example:

import Elder from 'elder-core'
import config from './config'

import CatAgeType from './types/cat-age'
import Cat from './models/cat'

export default Elder.create({
  config,
  types: {
    'cat-age': CatAgeType
  },
  models: {
    cat: Cat
  }
})

Basic Usage

We should now have an Elder ORM setup and ready for use. For the purposes of demonstration, we can create an index.ts file at the root of our project where we can import our ORM and start using it.

Create index.ts in the project root so that your project looks like the following

Example

/my-project
  /orm
    /models
    /types
    config.ts
    index.ts
  index.ts <-- add this file

Inside index.ts we should now be able to import our ORM and start using it's API.

Example

// my-project/index.ts
import orm from './orm'
const Cat = orm.models.cat

/* Cat.all().then(...) */