Sequelize ORM manager adapter for @storehouse/core. Provides seamless integration with SQL databases using the Sequelize ORM.
npm install @storehouse/core sequelize @storehouse/sequelize
You'll also need to install the driver for your database:
# PostgreSQL
npm install pg pg-hstore
# MySQL / MariaDB
npm install mysql2
# SQLite
npm install sqlite3
# SQL Server
npm install tedious
models/movie.ts
import {
DataTypes,
Model,
ModelStatic,
ModelAttributes,
ModelOptions,
Optional
} from 'sequelize';
import { ModelSettings } from '@storehouse/sequelize';
interface MovieAttributes {
id: number;
title: string;
rate?: number | null;
}
type MovieCreationAttributes = Optional<MovieAttributes, 'id'>;
interface MovieInstance
extends Model<MovieAttributes, MovieCreationAttributes>, MovieAttributes { }
const movieSchema: ModelAttributes<MovieInstance, MovieAttributes> = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
rate: {
type: DataTypes.TINYINT,
validate: {
max: 5,
min: 1,
isInt: true
}
}
};
const movieOptions: ModelOptions<Model<MovieAttributes, MovieCreationAttributes>> = {
modelName: 'movies',
tableName: 'movies'
};
export const movieSettings: ModelSettings<MovieAttributes, MovieCreationAttributes> = {
attributes: movieSchema,
options: movieOptions
};
index.ts
import { Storehouse } from '@storehouse/core';
import { SequelizeManager } from '@storehouse/sequelize';
import { movieSettings } from './models/movie';
// Register the manager
Storehouse.add({
local: {
type: SequelizeManager,
config: {
// Sequelize connection options
options: {
dialect: 'postgres',
host: 'localhost',
port: 5432,
database: 'mydb',
username: 'postgres',
password: 'password',
logging: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
},
// Model definitions
models: [movieSettings]
}
}
});
import { Storehouse } from '@storehouse/core';
import { SequelizeManager } from '@storehouse/sequelize';
import { ModelStatic } from 'sequelize';
import { MovieInstance } from './models/movie';
// Get the manager
const manager = Storehouse.getManager<SequelizeManager>('local');
if (manager) {
// Get the model
const Movies = manager.getModel<ModelStatic<MovieInstance>>('movies');
// Create a record
const movie = await Movies.create({
title: 'The Matrix',
rate: 5
});
console.log('Created:', movie.toJSON());
// Query records
const allMovies = await Movies.findAll({
where: { rate: 5 }
});
console.log('Found:', allMovies.length);
// Use raw queries
const [results] = await manager.query('SELECT * FROM movies WHERE rate > ?', {
replacements: [3]
});
console.log('Results:', results);
}
The config object passed to the manager contains:
options? - Sequelize connection options:
dialect: Database type ('mysql', 'postgres', 'sqlite', 'mariadb', 'mssql')host: Database hostport: Database portdatabase: Database nameusername: Database userpassword: Database passwordlogging: Logging function or false to disablepool: Connection pool configurationmodels? - Array of ModelSettings objects containing:
attributes: Model attribute definitionsoptions?: Model options (modelName, tableName, timestamps, etc.)model?: Optional pre-defined model class extending ModelThe package provides helper functions that throw errors instead of returning undefined, making your code cleaner and safer.
getManager()Retrieves a SequelizeManager instance from the registry.
import { Storehouse } from '@storehouse/core';
import { getManager } from '@storehouse/sequelize';
const manager = getManager(Storehouse, 'local');
await manager.sync();
Throws:
ManagerNotFoundError - If the manager doesn't existInvalidManagerConfigError - If the manager is not a SequelizeManager instancegetConnection()Retrieves the Sequelize connection instance.
import { Storehouse } from '@storehouse/core';
import { getConnection } from '@storehouse/sequelize';
import { QueryTypes } from 'sequelize';
const sequelize = getConnection(Storehouse, 'local');
const users = await sequelize.query('SELECT * FROM users', {
type: QueryTypes.SELECT
});
Throws:
ManagerNotFoundError - If the manager doesn't existInvalidManagerConfigError - If the connection is not a Sequelize instancegetModel()Retrieves a model from the registry.
import { Storehouse } from '@storehouse/core';
import { getModel } from '@storehouse/sequelize';
import { ModelStatic } from 'sequelize';
import { MovieInstance } from './models/movie';
// Get model from default manager
const Movies = getModel<MovieInstance>(Storehouse, 'movies');
// or
// const Movies = getModel<ModelStatic<MovieInstance>>(Storehouse, 'movies');
// Get model from specific manager
const MoviesFromLocal = getModel<MovieInstance>(Storehouse, 'local', 'movies');
// or
// const MoviesFromLocal = getModel<ModelStatic<MovieInstance>>(Storehouse, 'local', 'movies');
Throws:
ModelNotFoundError - If the model doesn't existThe SequelizeManager extends the Sequelize class with additional Storehouse integration features.
getConnection(): SequelizeReturns the Sequelize instance (the manager itself).
const sequelize = manager.getConnection();
await sequelize.sync();
closeConnection(): Promise<void>Closes the database connection and terminates all connections in the pool.
await manager.closeConnection();
getModel<M>(name): ModelStatic<M>Retrieves a model that has been defined in this Sequelize instance.
const Movies = manager.getModel<MovieInstance>('movies');
const movie = await Movies.findByPk(1);
isConnected(): Promise<boolean>Checks if the database connection is active by running an authentication test.
const connected = await manager.isConnected();
if (connected) {
console.log('Database is connected');
}
healthCheck(): Promise<SequelizeHealthCheckResult>Performs a comprehensive health check including connectivity test and metadata.
const health = await manager.healthCheck();
if (health.healthy) {
console.log(`✓ Sequelize is healthy`);
console.log(` Database: ${health.details.dialect} v${health.details.databaseVersion}`);
console.log(` Latency: ${health.details.latency}`);
console.log(` Models: ${health.details.models?.join(', ')}`);
} else {
console.error(`✗ Sequelize is unhealthy: ${health.message}`);
}
The health check returns a detailed result object:
healthy: boolean - Overall health statusmessage: string - Descriptive message about the health statustimestamp: number - Timestamp when the health check was performedlatency: number - Response time in millisecondsdetails: object - Detailed connection information
name: string - Manager namedialect: string - Database dialectdatabaseVersion?: string - Database versionmodels?: string[] - Array of registered model namesmodelCount?: number - Total number of modelslatency?: string - Response time in mserror?: string - Error details (if unhealthy)For more advanced use cases, you can define model classes that extend Sequelize's Model:
import {
DataTypes,
Model,
ModelAttributes,
ModelOptions,
Optional
} from 'sequelize';
import { ModelSettings } from '@storehouse/sequelize';
interface MovieAttributes {
id: number;
title: string;
rate?: number | null;
}
type MovieCreationAttributes = Optional<MovieAttributes, 'id'>;
class Movie extends Model<MovieAttributes, MovieCreationAttributes> implements MovieAttributes {
id!: number;
title!: string;
rate?: null | number;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
// Custom static method
static createMovie(title: string, rate?: number): Promise<Movie> {
return Movie.create({ title, rate });
}
// Custom instance getter
get fullTitle(): string {
return `${this.id}: ${this.title}`;
}
}
type MovieCtor = typeof Movie & { new(): Movie };
const movieSchema: ModelAttributes<Movie, MovieAttributes> = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
rate: {
type: DataTypes.TINYINT,
validate: {
max: 5,
min: 1,
isInt: true
}
}
};
const movieOptions: ModelOptions<Model<MovieAttributes, MovieCreationAttributes>> = {
modelName: 'movies',
tableName: 'movies'
};
export const movieSettings: ModelSettings<MovieAttributes, MovieCreationAttributes> = {
model: Movie, // Provide the model class
attributes: movieSchema,
options: movieOptions
};
Usage:
import { getModel } from '@storehouse/sequelize';
const Movies = getModel<MovieCtor>(Storehouse, 'local', 'movies');
const movie = await Movies.createMovie('Blade', 5);
console.log(movie.fullTitle); // "1: Blade"
You can register multiple database connections:
import { Storehouse } from '@storehouse/core';
import { SequelizeManager } from '@storehouse/sequelize';
Storehouse.add({
primary: {
type: SequelizeManager,
config: {
options: {
dialect: 'postgres',
host: 'localhost',
database: 'maindb',
username: 'postgres',
password: 'password'
},
models: [/* ... */]
}
},
analytics: {
type: SequelizeManager,
config: {
options: {
dialect: 'mysql',
host: 'analytics.example.com',
database: 'analyticsdb',
username: 'readonly',
password: 'password'
},
models: [/* ... */]
}
}
});
// Access specific managers
const primaryManager = getManager(Storehouse, 'primary');
const analyticsManager = getManager(Storehouse, 'analytics');
Set the manager type to simplify configuration and use string identifiers instead of class references:
import { Storehouse } from '@storehouse/core';
import { SequelizeManager } from '@storehouse/sequelize';
// Set default manager type
Storehouse.setManagerType(SequelizeManager);
// Now you can use type string instead of class
Storehouse.add({
local: {
type: '@storehouse/sequelize',
config: {
options: {
dialect: 'mysql',
host: 'localhost',
database: 'mydb',
username: 'root',
password: 'password'
},
models: []
}
}
});
import { Storehouse } from '@storehouse/core';
import { getConnection } from '@storehouse/sequelize';
const sequelize = getConnection(Storehouse, 'local');
// Managed transaction
await sequelize.transaction(async (t) => {
await Movies.create({
title: 'New Movie',
rate: 4
}, { transaction: t });
await Reviews.create({
movieId: 1,
text: 'Great!'
}, { transaction: t });
// Automatically commits or rolls back
});
// Unmanaged transaction
const t = await sequelize.transaction();
try {
await Movies.create({ title: 'New Movie', rate: 4 }, { transaction: t });
await Reviews.create({ movieId: 1, text: 'Great!' }, { transaction: t });
await t.commit();
} catch (error) {
await t.rollback();
throw error;
}
If you don't specify the logging option, you can enable the default logs using the @novice1/logger package:
import { Debug } from '@novice1/logger';
Debug.enable('@storehouse/sequelize*');
Or disable logging entirely:
Storehouse.add({
local: {
type: SequelizeManager,
config: {
options: {
// ...
logging: false
}
}
}
});
The package is written in TypeScript and provides full type definitions for type-safe operations:
import { Storehouse } from '@storehouse/core';
import { ModelStatic } from 'sequelize';
import { SequelizeManager, getManager, getModel } from '@storehouse/sequelize';
// Typed manager
const manager = getManager<SequelizeManager>(Storehouse, 'local');
// Typed models
interface User {
id: number;
name: string;
email: string;
}
const Users = getModel<User>(Storehouse, 'local', 'users');
// Type-safe query results
const users = await Users.findAll();
// users is typed as Model<User>[]
users.forEach(user => {
console.log(user.name); // Fully typed
});
// Type-safe creation
const newUser = await Users.create({
name: 'John Doe',
email: 'john@example.com'
});
All helper functions throw specific errors for better error handling:
import { Storehouse } from '@storehouse/core';
import { getManager, getModel } from '@storehouse/sequelize';
import {
ManagerNotFoundError,
InvalidManagerConfigError,
ModelNotFoundError
} from '@storehouse/core';
try {
const manager = getManager(Storehouse, 'nonexistent');
} catch (error) {
if (error instanceof ManagerNotFoundError) {
console.error('Manager not found:', error.message);
} else if (error instanceof InvalidManagerConfigError) {
console.error('Invalid manager type:', error.message);
}
}
try {
const Movies = getModel(Storehouse, 'nonexistent', 'movies');
} catch (error) {
if (error instanceof ModelNotFoundError) {
console.error('Model not found:', error.message);
}
}
closeConnection() when shutting downlogging: false or use conditional loggingMIT