postgres-schema-builder v1.1.0-beta.4
postgres-schema-builder
Simple postgres schema builder for Node.JS leveraging Typescript's type system to enable typesafe queries
Installation
npm i postgres-schema-builder
or yarn add postgres-schema-builder
Usage
If you need a reference project which uses postgres-schema-builder
, have a look at musicshare.
Schema definition
The recommended way to define your database schema is to export a namespace indicating the schema version. The namespace itself contains the table definitions.
// DatabaseV1.ts
import { TableSchema, ColumnType, NativeFunction, ForeignKeyUpdateDeleteRule, JSONType } from "postgres-schema-builder"
export namespace DatabaseV1 {
const baseSchema = TableSchema({
date_added: { type: ColumnType.TimestampTZ, nullable: false, defaultValue: { func: NativeFunction.Now } },
date_removed: { type: ColumnType.TimestampTZ, nullable: true },
})
export const users = TableSchema({
...baseSchema,
user_id: { type: ColumnType.Integer, primaryKey: true, unique: true },
name: { type: ColumnType.Varchar, nullable: false },
settings: { type: JSONType<ISomeSettingsInterface>(), nullable: false },
})
export const user_emails = TableSchema({
user_id_ref: {type: ColumnType.Integer, primaryKey: true, nullable: false, foreignKeys: [{ targetTable: 'users', targetColumn: 'user_id', onDelete: ForeignKeyUpdateDeleteRule.Cascade }]},
email: { type: ColumnType.Varchar, primaryKey: true, nullable: false },
})
// ...more tables
}
All available ColumnType
s can be found in the table.ts file
.
Interfaces and Table API
After defining the tables of our schema, we can create and export an interface for each table, which contains each column as key
, as well as the respective TypeScript type infered from the column's ColumnType
.
Furthermore, we can also create a table object for each table entry of our schema, which provides useful API methods for typesafe queries.
// tables.ts
import { TableRecord, Table } from "postgres-schema-builder"
import { DatabaseV1 } from "./DatabaseV1"
export const Tables = DatabaseV1
export interface IUserDBResult extends TableRecord<typeof Tables.users> { }
export interface IUserEMailDBResult extends TableRecord<typeof Tables.user_emails> { }
// ...more interfaces, for each table one interface
export const UsersTable = Table(Tables, 'users')
export const UserEMailsTable = Table(Tables, 'user_emails')
// ...more table objects, for each table one object
Queries
Now, we can use our exported table objects to create typesafe queries.
import { SQL } from "postgres-schema-builder"
import { UsersTable, UserEMailsTable} from "./tables.ts"
UsersTable.create() // table create statement
UsersTable.drop() // table drop statement
UsersTable.insert(['name'])(['Fresh Herrmann']) // insert new entry
UsersTable.insert(['name'])([null]) // compiler error, since name is not nullable
UsersTable.insertFromObj({
name: 'Fresh Herrmann',
date_added: new Date(),
settings: {a: 42, b: 'no'},
})
UsersTable.select('*', ['user_id'])([42]) // select all columns where user_id=42
UsersTable.select(['user_id', 'name'], ['name'])(['Fresh Herrmann']) // select only user_id and name where name='Fresh Herrmann'
UsersTable.selectAll('*') // select all entries from users
UsersTable.selectAll(['name']) // select all names from users
UsersTable.update(['name'], ['user_id'])(['Freshly Fresh Herrmann'], [42]) // update entry's name where user_id=42
UsersTable.delete(['user_id'])([1]) // delete where user_id=1
UsersTable.delete(['user_id'])(['abcd']) // compiler error, since user_id has type number
// create custom query using a join
const query = SQL.raw<typeof Tables.users & typeof Tables.user_emails>(`
SELECT *
FROM ${UsersTable.name} u
INNER JOIN ${UserEMailsTable.name} e ON u.user_id = e.user_id_ref
WHERE u.date_removed IS NULL
AND u.user_id = $1;
`, [42])
Database Client
postgres-schema-builder
also provides a small database client to perform our typesafe and custom queries.
import { DatabaseClient } from "postgres-schema-builder"
import { Pool } from "pg"
import { UsersTable, Tables} from "./tables.ts"
import { config } from "./some-config.ts"
const database = DatabaseClient(
new Pool({
host: config.database.host,
port: config.database.port,
user: config.database.user,
password: config.database.password,
database: config.database.database,
})
);
// single query statements
await database.query(
UsersTable.create()
)
await database.query(
UsersTable.insertFromObj({
name: 'Fresh Herrmann',
date_added: new Date(),
settings: { a: 42, b: 'no' },
})
)
const dbResults = await database.query(UsersTable.selectAll('*'))
// batch queries
const insertStatements = someDataArray.map(entry => UsersTable.insertFromObj(entry))
await database.batch(insertStatements)
// leverage transaction creating your database schema
const createTableStatements = composeCreateTableStatements(Tables) // performs a topological sort on your tables defined in <Tables>
await database.transaction(async (client) => {
createTableStatements.forEach(createTableStatement => client.query({ sql: createTableStatement }))
});
Todos
- Improve and extend docs
- Allow
insert
andinsertFromObj
returning the inserted data - Enable client to perform multiple queries
- Introduce database management object enabling schema versioning with migrations (try out v1.1.0-beta.0)
- Extend test cases and improve code coverage
Support
Node.js
Currently, this package is automatically tested under Node.js versions 8 - 13
.
All build artifact are compiled to ES6.
PostgreSQL
Tested under v9.6
, might work for newer versions as well.
Contributors
- Yannick Stachelscheid (@yss14)
License
This project is licensed under the MIT license.
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago