douze-sequelize v0.3.3
douze-sequelize
Use Sequelize in Douze to connect to SQL databases.
Features
- Typings with
sequelize-typescript
- Multiple databases in the same application
- Migrations with
umzug
- Seeding function
Installation
Install douze-sequelize
along with your desired database driver:
pg pg-hstore
for PostgreSQLmysql2
for MySQLmariadb
for MariaDBsqlite3
for SQLitetedious
for Microsoft SQL Server (MSSQL)
Example for Postgres:
$ yarn add douze-sequelize pg pg-hstore
# or
$ npm i douze-sequelize pg pg-hstore
Usage
Import it into your app and extend
Douze with it:
import Douze from 'douze'
import douzeSequelize from 'douze-sequelize'
const douze = new Douze()
douze.extend(douzeSequelize())
async function main() {
const app = douze.createApp()
await douze.start(app)
}
if (require.main === module) {
main()
}
Configuration
You can configure the name of the environment variable used for the connection URI, along with the paths where to load models and migrations:
douze.extend(
douzeSequelize({
envNames: {
// uri will be read from process.env.SERVICES_POSTGRES_URI
uri: 'SERVICES_POSTGRES_URI'
},
paths: {
models: [path.join(__dirname, './db/models')]
},
options: {
// Pass more options to the Sequelize constructor here
...
}
})
)
Models definition
You can follow the documentation of sequelize-typescript
,
with one catch: as noted in this issue,
models have to use the same package as the one with which the Sequelize
instance is created (ie: the one used in douze-sequelize
).
To solve that and minimize the amount of dependency-juggling,
douze-sequelize
re-exports all definitions from sequelize-typescript
,
so you can do:
- import { Model, Table, Column } from 'sequelize-typescript'
+ import { Model, Table, Column } from 'douze-sequelize'
Accessing the Sequelize instance
The Sequelize instance can be found in your app, at
app.locals.sequelize
:
import Douze from 'douze'
import douzeSequelize from 'douze-sequelize'
const douze = new Douze()
douze.extend(douzeSequelize())
export default async function main() {
const app = douze.createApp()
// Access the sequelize instance
await app.locals.sequelize.authenticate()
// Note: douze.start will do the `authenticate` part for you
// and handle / log errors.
await douze.start(app)
}
Multiple databases
If you want to connect to multiple databases, you can call extend
as
many times as you want with different configurations.
However, to differenciate the sequelize instances, you'll have to give them unique names:
import path from 'path'
import Douze from 'douze'
import douzeSequelize from 'douze-sequelize'
const douze = new Douze()
douze.extend(
douzeSequelize({
name: 'pg',
envNames: {
uri: 'SERVICES_POSTGRES_URI'
},
paths: {
models: [path.join(__dirname, './db/pg/models')]
}
})
)
douze.extend(
douzeSequelize({
name: 'sqlite',
type: 'sqlite', // only needed for SQLite
envNames: {
// URI to a file path is optional on SQLite,
// if unset at runtime it will use :memory:
uri: 'SERVICES_SQLITE_FILE_PATH'
},
paths: {
models: [path.join(__dirname, './db/sqlite/models')]
}
})
)
export default async function main() {
const app = douze.createApp()
// Access the sequelize instances by their names:
await app.locals.pg.authenticate()
await app.locals.sqlite.authenticate()
await douze.start(app)
}
Migrations
Migrations are a way to run modifications to the database structure in production.
douze-sequelize
uses umzug
for migration definitions, and stores the migration state in the database
itself.
- Declare the path of the directory where you will store your migration files:
douze.extend(douzeSequelize(
paths: {
migrationsDirectory: path.join(__dirname, './db/migrations')
}
))
- Write a migration definition file:
// db/migrations/0001-name-of-migration.ts
import { MigrationArgs } from 'douze-sequelize'
export const up = async ({ douze, app, sequelize }: MigrationArgs) => {
// Place your 'forward' modifications here
}
export const down = async ({ douze, app, sequelize }: MigrationArgs) => {
// Place your 'rollback' modifications here
}
Extra configuration
You can pass additional configuration to umzug as shown:
douze.extend(douzeSequelize(
paths: {
migrationsDirectory: path.join(__dirname, './db/migrations')
},
umzug: { // Umzug configuration object
migrations: {
// Eg: add extra arguments to the up/down migration functions:
wrap: f => (args: MigrationArgs) => f({ ...args, foo: 'bar' }),
}
}
))
- Run your migrations with the
{name}:migrate
task:
import Douze from 'douze'
import douzeSequelize from 'douze-sequelize'
const douze = new Douze()
douze.extend(
douzeSequelize({
name: 'mysql'
})
)
const app = douze.createApp()
douze.invokeTask('mysql:migrate', app)
Tasks
This plugin exposes the following tasks, where {name}
is the value of
the name
field in the options, or sequelize
by default:
{name}:init
- Initialize the database, force-syncing models and dropping all data.{name}:seed
- Run seeding functions (not implemented yet){name}:migrate
- Execute all pending migrations{name}:rollback
- Rollback the latest migration (if any)
Environment variables
This plugin exposes the following environment variables for runtime configuration:
DOUZE_SEQUELIZE_CONNECTION_RETRIES
: optional, defines the number of retries allowed to establish the connection to the database. Note that up to N+1 attempts will be performed (the initial attempt + N retries). Defaults to 7 retries (8 attempts) if unset.DOUZE_SEQUELIZE_CONNECTION_RETRY_DELAY_MS
: optional, defines the delay in milliseconds before the first retry. Exponential backout strategy is used, so this delay is doubled for each retry. Defaults to 150ms if unset.DOUZE_SEQUELIZE_FORCE_SYNC
: optional, set totrue
to drop all data and recreate the database structure on application startup. Has an effect only in development.DOUZE_DATABASE_NO_MIGRATIONS
: optional, set totrue
to skip running migrations on application startup. Has an effect only in production.
Roadmap
Features and stories required to reach 1.0.0:
- Retry logic for initial connection
- Migrations in production
- Seeding