
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
pg-schema-builder
Advanced tools
SchemaBuilder - это инструмент для управления схемой базы данных PostgreSQL в Node.js с поддержкой TypeScript. Он предоставляет удобный API для создания, изменения и удаления таблиц, колонок, индексов и других объектов базы данных.
Для использования SchemaBuilder необходимо создать экземпляр класса, передав ему объект подключения к базе данных PostgreSQL:
import { Pool } from 'pg';
import SchemaBuilder from './schema/SchemaBuilder.js';
// Создаем пул подключений к базе данных
const pool = new Pool({
user: 'postgres',
host: 'localhost',
database: 'mydb',
password: 'password',
port: 5432,
});
// Создаем экземпляр SchemaBuilder
const schema = new SchemaBuilder(pool, {
schemaName: 'public', // Имя схемы (по умолчанию 'public')
migrationsTable: 'migrations' // Имя таблицы миграций (по умолчанию 'migrations')
});
Для создания таблицы используется метод createTable:
await schema.createTable('users', (table) => {
table.increments('id'); // Создает колонку id типа SERIAL с PRIMARY KEY
table.string('name', 100).notNullable();
table.string('email', 255).notNullable().unique();
table.string('password', 255).notNullable();
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
table.timestamp('updated_at').defaultTo('CURRENT_TIMESTAMP');
});
SchemaBuilder поддерживает следующие типы данных:
table.increments('id'); // SERIAL PRIMARY KEY
table.integer('count'); // INTEGER
table.string('name', 255); // VARCHAR(255)
table.text('description'); // TEXT
table.timestamp('created_at'); // TIMESTAMP WITH TIME ZONE
table.boolean('is_active'); // BOOLEAN
table.json('metadata'); // JSONB
table.date('birth_date'); // DATE
table.uuid('id'); // UUID
table.decimal('price', 8, 2); // DECIMAL(8,2)
table.float('rate'); // FLOAT
table.double('amount'); // DOUBLE PRECISION
table.bigInteger('big_number'); // BIGINT
table.smallInteger('small_number'); // SMALLINT
table.time('meeting_time'); // TIME
table.array('tags', 'text'); // TEXT[]
table.jsonType('data'); // JSON (не JSONB)
table.enum('status', ['active', 'inactive', 'pending']); // TEXT с CHECK ограничением
Для колонок можно указать различные модификаторы:
table.string('username')
.notNullable() // NOT NULL
.unique() // UNIQUE
.defaultTo('guest') // DEFAULT 'guest'
.primary(); // PRIMARY KEY
// Для внешних ключей
table.integer('user_id')
.references('id') // REFERENCES users(id)
.inTable('users')
.onDelete('CASCADE') // ON DELETE CASCADE
.onUpdate('CASCADE'); // ON UPDATE CASCADE
Для изменения существующей таблицы используется метод alterTable:
await schema.alterTable('users', (table) => {
// Добавление новой колонки
table.addString('phone', 20);
// Добавление колонки с ограничениями
table.addInteger('role_id')
.notNullable()
.defaultTo(1)
.references('id')
.inTable('roles')
.onDelete('CASCADE');
// Удаление колонки
table.dropColumn('old_column');
// Переименование колонки
table.renameColumn('old_name', 'new_name');
});
Для создания индексов можно использовать методы index и uniqueIndex :
// При создании таблицы
await schema.createTable('users', (table) => {
table.increments('id');
table.string('email', 255).notNullable();
table.string('username', 100).notNullable();
// Создание индекса
table.index('email');
// Создание индекса с указанием имени
table.index('username', 'idx_username');
// Создание составного индекса
table.index(['email', 'username']);
// Создание уникального индекса
table.uniqueIndex('email', 'uniq_email');
});
// Или отдельно после создания таблицы
await schema.createIndexIfNotExists('idx_users_email', 'users', 'email');
Внешние ключи можно создавать как при создании таблицы, так и при ее изменении:
// При создании таблицы
await schema.createTable('posts', (table) => {
table.increments('id');
table.string('title', 255).notNullable();
table.text('content');
table.integer('user_id').notNullable()
.references('id')
.inTable('users')
.onDelete('CASCADE')
.onUpdate('CASCADE');
});
// При изменении таблицы
await schema.alterTable('comments', (table) => {
table.foreign('post_id', 'posts', 'id')
.onDelete('CASCADE')
.onUpdate('CASCADE');
});
SchemaBuilder поддерживает систему миграций для управления изменениями схемы базы данных:
// Пример файла миграции
// 20240101000001_create_users_table.js
export default {
name: '20240101000001_create_users_table',
async up(schema) {
await schema.createTable('users', (table) => {
table.increments('id');
table.string('name', 100).notNullable();
table.string('email', 255).notNullable().unique();
table.string('password', 255).notNullable();
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
table.timestamp('updated_at').defaultTo('CURRENT_TIMESTAMP');
});
},
async down(schema) {
await schema.dropTable('users');
}
};
Для выполнения миграций используются методы:
// Создание таблицы миграций, если она не существует
await schema.createMigrationsTable();
// Получение списка выполненных миграций
const migrations = await schema.getMigrations();
// Выполнение миграции
await schema.runMigration(migration, batch);
// Откат миграции
await schema.rollbackMigration(migration);
Для выполнения операций в транзакции используется метод transaction :
await schema.transaction(async (client) => {
// Создаем временный SchemaBuilder с клиентом транзакции
const transactionSchema = new SchemaBuilder(client);
// Выполняем операции
await transactionSchema.createTable('users', (table) => {
table.increments('id');
table.string('name', 100).notNullable();
});
await transactionSchema.createTable('posts', (table) => {
table.increments('id');
table.string('title', 255).notNullable();
table.integer('user_id').notNullable()
.references('id')
.inTable('users')
.onDelete('CASCADE');
});
});
Для создания представлений и материализованных представлений используются методы createView и createMaterializedView:
// Создание представления
await schema.createView('active_users', `
SELECT * FROM users WHERE is_active = true
`);
// Создание материализованного представления
await schema.createMaterializedView('user_statistics', `
SELECT
COUNT(*) as total_users,
SUM(CASE WHEN is_active = true THEN 1 ELSE 0 END) as active_users,
SUM(CASE WHEN is_active = false THEN 1 ELSE 0 END) as inactive_users
FROM users
`);
Для создания функций и триггеров используются методы createFunction и createTrigger :
// Создание функции
await schema.createFunction(
'update_updated_at',
[],
'trigger',
`
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
`
);
// Создание триггера
await schema.createTrigger(
'users_updated_at_trigger',
'users',
'BEFORE',
'UPDATE',
'update_updated_at'
);
SchemaBuilder может генерировать код моделей на основе существующих таблиц:
// Генерация модели для таблицы users
const userModelCode = await schema.generateModel('users', 'User');
console.log(userModelCode);
// Проверка существования таблицы
const tableExists = await schema.hasTable('users');
// Проверка существования колонки в таблице
const columnExists = await schema.hasColumn('users', 'email');
// Выполнение произвольного SQL-запроса
await schema.raw(`
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
`);
// Создание последовательности
await schema.createSequence('my_sequence', {
start: 1000,
increment: 10,
minValue: 1000,
maxValue: 9999999,
cache: 10,
cycle: true
});
// Получение информации о схеме таблицы
const tableSchema = await schema.getTableSchema('users');
console.log(tableSchema.columns);
console.log(tableSchema.primaryKey);
console.log(tableSchema.foreignKeys);
console.log(tableSchema.indexes);
// Выполнение запроса с кэшированием
const result = await schema.query('SELECT * FROM users WHERE id = $1', [1], true);
// Очистка кэша запросов
schema.clearCache();
Начиная с версии 1.0.0, SchemaBuilder полностью поддерживает TypeScript, что обеспечивает статическую типизацию и улучшенную поддержку IDE.
import { Pool } from 'pg';
import { SchemaBuilder, TableBuilder } from 'schema-builder';
// Создаем пул подключений к базе данных
const pool = new Pool({
user: 'postgres',
host: 'localhost',
database: 'mydb',
password: 'password',
port: 5432,
});
// Создаем экземпляр SchemaBuilder с типизированными опциями
const schema = new SchemaBuilder(pool, {
schemaName: 'public',
migrationsTable: 'migrations'
});
// Типизированные методы и параметры
async function createUsersTable() {
await schema.createTable('users', (table: TableBuilder) => {
table.increments('id');
table.string('name', 100).notNullable();
table.string('email', 255).notNullable().unique();
table.boolean('active').defaultTo(true);
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
});
}
Библиотека поставляется в нескольких форматах:
require() в Node.jsimport в современных проектах.d.ts файлы для статической типизацииЕсли вы хотите собрать библиотеку из исходного кода:
# Клонирование репозитория
git clone https://github.com/your-username/schema-builder.git
cd schema-builder
# Установка зависимостей
npm install
# Сборка библиотеки
npm run build
Команда npm run build выполнит следующие действия:
dist.d.ts)schema-builder/
├── dist/ # Скомпилированные файлы
│ ├── cjs/ # CommonJS формат
│ ├── esm/ # ESM формат
│ └── types/ # TypeScript типы
├── src/ # Исходный код
│ ├── AlterTableBuilder.ts
│ ├── ColumnBuilder.ts
│ ├── SchemaBuilder.ts
│ ├── TableBuilder.ts
│ └── index.ts # Точка входа
├── package.json # Метаданные пакета
├── tsconfig.json # Базовая конфигурация TypeScript
├── tsconfig.cjs.json # Конфигурация для CommonJS
├── tsconfig.esm.json # Конфигурация для ESM
└── tsconfig.types.json # Конфигурация для типов
## Примеры использования
### Пример 1: Создание базы данных для блога
```javascript
// Создание таблицы пользователей
await schema.createTable('users', (table) => {
table.increments('id');
table.string('name', 100).notNullable();
table.string('email', 255).notNullable().unique();
table.string('password', 255).notNullable();
table.boolean('is_admin').defaultTo(false);
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
table.timestamp('updated_at').defaultTo('CURRENT_TIMESTAMP');
});
// Создание таблицы категорий
await schema.createTable('categories', (table) => {
table.increments('id');
table.string('name', 100).notNullable().unique();
table.string('slug', 100).notNullable().unique();
table.text('description');
});
// Создание таблицы статей
await schema.createTable('posts', (table) => {
table.increments('id');
table.string('title', 255).notNullable();
table.string('slug', 255).notNullable().unique();
table.text('content').notNullable();
table.integer('user_id').notNullable()
.references('id')
.inTable('users')
.onDelete('CASCADE');
table.integer('category_id')
.references('id')
.inTable('categories')
.onDelete('SET NULL');
table.boolean('is_published').defaultTo(false);
table.timestamp('published_at');
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
table.timestamp('updated_at').defaultTo('CURRENT_TIMESTAMP');
// Создание индексов
table.index('user_id');
table.index('category_id');
table.index('published_at');
});
// Создание таблицы комментариев
await schema.createTable('comments', (table) => {
table.increments('id');
table.text('content').notNullable();
table.integer('post_id').notNullable()
.references('id')
.inTable('posts')
.onDelete('CASCADE');
table.integer('user_id').notNullable()
.references('id')
.inTable('users')
.onDelete('CASCADE');
table.integer('parent_id')
.references('id')
.inTable('comments')
.onDelete('CASCADE');
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
table.timestamp('updated_at').defaultTo('CURRENT_TIMESTAMP');
// Создание индексов
table.index('post_id');
table.index('user_id');
table.index('parent_id');
});
// Создание таблицы тегов
await schema.createTable('tags', (table) => {
table.increments('id');
table.string('name', 50).notNullable().unique();
table.string('slug', 50).notNullable().unique();
});
// Создание связующей таблицы между статьями и тегами
await schema.createTable('post_tags', (table) => {
table.integer('post_id').notNullable()
.references('id')
.inTable('posts')
.onDelete('CASCADE');
table.integer('tag_id').notNullable()
.references('id')
.inTable('tags')
.onDelete('CASCADE');
// Создание составного первичного ключа
table.primary(['post_id', 'tag_id']);
// Создание индексов
table.index('post_id');
table.index('tag_id');
});
// Добавление новых колонок в таблицу пользователей
await schema.alterTable('users', (table) => {
table.addString('phone', 20);
table.addString('avatar', 255);
table.addJson('settings');
});
// Добавление статистики просмотров для статей
await schema.alterTable('posts', (table) => {
table.addInteger('views').notNullable().defaultTo(0);
});
// Создание таблицы для отслеживания просмотров статей
await schema.createTable('post_views', (table) => {
table.increments('id');
table.integer('post_id').notNullable()
.references('id')
.inTable('posts')
.onDelete('CASCADE');
table.string('ip_address', 45).notNullable();
table.string('user_agent', 255);
table.timestamp('viewed_at').defaultTo('CURRENT_TIMESTAMP');
// Создание индексов
table.index('post_id');
table.index('ip_address');
table.index('viewed_at');
});
// Создание функции для обновления счетчика просмотров
await schema.createFunction(
'increment_post_views',
[],
'trigger',
`
UPDATE posts
SET views = views + 1
WHERE id = NEW.post_id;
RETURN NEW;
`
);
// Создание триггера для автоматического обновления счетчика просмотров
await schema.createTrigger(
'post_views_increment_trigger',
'post_views',
'AFTER',
'INSERT',
'increment_post_views'
);
// Перенос данных из одной таблицы в другую с использованием транзакции
await schema.transaction(async (client) => {
const transactionSchema = new SchemaBuilder(client);
// Создание новой таблицы
await transactionSchema.createTable('users_new', (table) => {
table.increments('id');
table.string('name', 100).notNullable();
table.string('email', 255).notNullable().unique();
table.string('password', 255).notNullable();
table.string('phone', 20);
table.timestamp('created_at').defaultTo('CURRENT_TIMESTAMP');
table.timestamp('updated_at').defaultTo('CURRENT_TIMESTAMP');
});
// Копирование данных
await transactionSchema.raw(`
INSERT INTO users_new (id, name, email, password, created_at, updated_at)
SELECT id, name, email, password, created_at, updated_at
FROM users
`);
// Удаление старой таблицы
await transactionSchema.dropTable('users');
// Переименование новой таблицы
await transactionSchema.raw(`ALTER TABLE users_new RENAME TO users`);
// Восстановление индексов
await transactionSchema.raw(`
CREATE INDEX idx_users_email ON users(email)
`);
});
Заключение
SchemaBuilder предоставляет мощный и гибкий API для управления схемой базы данных PostgreSQL. Он позволяет создавать, изменять и удалять таблицы, колонки, индексы и другие объекты базы данных с помощью удобного и интуитивно понятного синтаксиса.
Для получения дополнительной информации о возможностях SchemaBuilder обратитесь к исходному коду и комментариям в файлах:
FAQs
Инструмент для управления схемой базы данных PostgreSQL в Node.js
We found that pg-schema-builder demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.