npm package: @seanmcgary/database
Description: Database boilerplate using Sequelize.
Latest version: 3.3.1

## @seanmcgary/database

Database boilerplate using Sequelize.

### Install

```bash
yarn add @seanmcgary/database
```

### Use

#### Usage caveats and notes due to some sequelize weirdness:

- Column and table names should be camelCased when used in JS/TS. Sequelize will automatically convert to/from snake_case since that is how things are represented in Postgres
- ```createdAt``` and ```updatedAt``` columns are automatically added for each table
- Each table will automatically get a column called ```id``` which will be a ```SERIAL``` type and set as the primary key. To override it, simple add an ```id``` column manually and give it a type.


#### Example directory structure and files:

```text
.
├── package.json
├── src
│   ├── db
│   │   └── index.ts
│   ├── migrations
│   │   └── 20180407084900-example-migration.ts
│   └── models
│       └── exampleUserModel.ts
├── tsconfig.json
└── yarn.lock

4 directories, 6 files
```

_src/db/index.ts_
```typescript
import { DB, MigrationConfig, DBConfig } from '@seanmcgary/database';
import * as path from 'path';

import exampleUserModel from '../models/exampleUserModel';

const databaseConfig = {
	username: 'some-username',
	password: 'some-password',
	database: 'example-db',
	host: '127.0.0.1'
};

const migrationConfig  = {
	path: path.normalize(`${__dirname}/../migrations`)
};

const db = new DB(
	<DBConfig>databaseConfig,
	<MigrationConfig>migrationConfig
);

db.init((db: DB) => {
	const User = db.setModel('User', db.loadModel(exampleUserModel));
});

db.migrate();

export default db;

```

_src/models/exampleUserModel.ts_
```typescript
import { Sequelize, ModelOptions, Model, DataTypes, BuildOptions, ModelAttributes } from 'sequelize';
import { ModelWrapper, StaticModel, AllowedFields, FieldMessages } from '@seanmcgary/database';

export interface UserInstanceFields {
	password: string;
	username: string;
	email: string;
}

// tslint:disable-next-line:no-any
export interface UserInstance extends UserInstanceFields, Model {
	doesPasswordMatch(storedPassword: string, providedPassword: string): boolean;
	toJSON(sanitize?: boolean): UserInstanceFields;
}

type UserInstanceStatic = StaticModel<UserInstance>;

export class User extends ModelWrapper<UserInstance, UserInstanceStatic> {
	constructor(db: Sequelize, modelName: string, attributes: ModelAttributes, options: ModelOptions) {
		super(db, modelName, attributes, options);
	}

	get allowedFields(): AllowedFields {
		return {
			create: ['email', 'password', 'username'],
			update: ['email', 'password', 'username']
		};
	}

	get fieldMessages(): FieldMessages {
		return User.fieldMessages;
	}

	static get fieldMessages(): FieldMessages {
		return {
			email: {
				invalid: 'Please provide a valid email address'
			},
			password: {
				invalid: 'Password must be at least 8 characters'
			},
			username: {
				invalid: 'Please provide a valid username',
				unique: 'That username is already taken'
			}
		};
	}
}

export default function(db: Sequelize): User {
	return new User(db, 'users', {
		email: {
			type: DataTypes.STRING,
			allowNull: true,
			defaultValue: null,
			validate: {
				isEmail: true,
				notEmpty: true
			}
		},
		username: {
			type: DataTypes.STRING,
			allowNull: false,
			validate: {
				notEmpty: true,
				len: [1, 128]
			},
			unique: true
		},
		password: {
			type: DataTypes.STRING,
			allowNull: false,
			validate: {
				len: [8, 255]
			}
		}
	}, {
		freezeTableName: true,
		timestamps: true
	});
}
```