Skip to content

Commit

Permalink
feature: extend readme with docs and meta info (#4)
Browse files Browse the repository at this point in the history
add installation, usage, todos, support, contributors, and license sections to readme
  • Loading branch information
yss14 authored Nov 19, 2019
1 parent 584e31d commit e27be34
Showing 1 changed file with 174 additions and 1 deletion.
175 changes: 174 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,175 @@
# postgres-schema-builder
Simple postgres schema builder leveraging Typescript's type system to enable typesafe queries
Simple postgres schema builder for Node.JS leveraging Typescript's type system to enable typesafe queries

[![Build Status](https://travis-ci.com/yss14/postgres-schema-builder.svg?branch=master)](https://travis-ci.com/yss14/postgres-schema-builder)
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=yss14/postgres-schema-builder)](https://dependabot.com)

## 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](https://github.com/yss14/musicshare/tree/master/projects/backend/src/database).

### 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.

```typescript
// 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`)[https://github.com/yss14/postgres-schema-builder/blob/master/src/table.ts#L78].

### 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.

```typescript
// 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.

```typescript
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.

```typescript
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` and `insertFromObj` returning the inserted data
* Enable client to perform multiple queries
* Introduce database management object enabling schema versioning with migrations
* 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](https://github.com/yss14))

## License
This project is licensed under the [MIT](LICENSE) license.

0 comments on commit e27be34

Please sign in to comment.