Skip to content

Commit e27be34

Browse files
authored
feature: extend readme with docs and meta info (#4)
add installation, usage, todos, support, contributors, and license sections to readme
1 parent 584e31d commit e27be34

1 file changed

Lines changed: 174 additions & 1 deletion

File tree

README.md

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,175 @@
11
# postgres-schema-builder
2-
Simple postgres schema builder leveraging Typescript's type system to enable typesafe queries
2+
Simple postgres schema builder for Node.JS leveraging Typescript's type system to enable typesafe queries
3+
4+
[![Build Status](https://travis-ci.com/yss14/postgres-schema-builder.svg?branch=master)](https://travis-ci.com/yss14/postgres-schema-builder)
5+
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=yss14/postgres-schema-builder)](https://dependabot.com)
6+
7+
## Installation
8+
`npm i postgres-schema-builder` or `yarn add postgres-schema-builder`
9+
10+
## Usage
11+
12+
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).
13+
14+
### Schema definition
15+
16+
The recommended way to define your database schema is to export a namespace indicating the schema version. The namespace itself contains the table definitions.
17+
18+
```typescript
19+
// DatabaseV1.ts
20+
import { TableSchema, ColumnType, NativeFunction, ForeignKeyUpdateDeleteRule, JSONType } from "postgres-schema-builder"
21+
22+
export namespace DatabaseV1 {
23+
const baseSchema = TableSchema({
24+
date_added: { type: ColumnType.TimestampTZ, nullable: false, defaultValue: { func: NativeFunction.Now } },
25+
date_removed: { type: ColumnType.TimestampTZ, nullable: true },
26+
})
27+
28+
export const users = TableSchema({
29+
...baseSchema,
30+
user_id: { type: ColumnType.Integer, primaryKey: true, unique: true },
31+
name: { type: ColumnType.Varchar, nullable: false },
32+
settings: { type: JSONType<ISomeSettingsInterface>(), nullable: false },
33+
})
34+
35+
export const user_emails = TableSchema({
36+
user_id_ref: {type: ColumnType.Integer, primaryKey: true, nullable: false, foreignKeys: [{ targetTable: 'users', targetColumn: 'user_id', onDelete: ForeignKeyUpdateDeleteRule.Cascade }]},
37+
email: { type: ColumnType.Varchar, primaryKey: true, nullable: false },
38+
})
39+
40+
// ...more tables
41+
}
42+
```
43+
44+
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].
45+
46+
### Interfaces and Table API
47+
48+
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`.
49+
50+
Furthermore, we can also create a table object for each table entry of our schema, which provides useful API methods for typesafe queries.
51+
52+
```typescript
53+
// tables.ts
54+
import { TableRecord, Table } from "postgres-schema-builder"
55+
import { DatabaseV1 } from "./DatabaseV1"
56+
57+
export const Tables = DatabaseV1
58+
59+
export interface IUserDBResult extends TableRecord<typeof Tables.users> { }
60+
export interface IUserEMailDBResult extends TableRecord<typeof Tables.user_emails> { }
61+
// ...more interfaces, for each table one interface
62+
63+
export const UsersTable = Table(Tables, 'users')
64+
export const UserEMailsTable = Table(Tables, 'user_emails')
65+
// ...more table objects, for each table one object
66+
```
67+
68+
### Queries
69+
70+
Now, we can use our exported table objects to create typesafe queries.
71+
72+
```typescript
73+
import { SQL } from "postgres-schema-builder"
74+
import { UsersTable, UserEMailsTable} from "./tables.ts"
75+
76+
UsersTable.create() // table create statement
77+
UsersTable.drop() // table drop statement
78+
79+
UsersTable.insert(['name'])(['Fresh Herrmann']) // insert new entry
80+
UsersTable.insert(['name'])([null]) // compiler error, since name is not nullable
81+
UsersTable.insertFromObj({
82+
name: 'Fresh Herrmann',
83+
date_added: new Date(),
84+
settings: {a: 42, b: 'no'},
85+
})
86+
87+
UsersTable.select('*', ['user_id'])([42]) // select all columns where user_id=42
88+
UsersTable.select(['user_id', 'name'], ['name'])(['Fresh Herrmann']) // select only user_id and name where name='Fresh Herrmann'
89+
90+
UsersTable.selectAll('*') // select all entries from users
91+
UsersTable.selectAll(['name']) // select all names from users
92+
93+
UsersTable.update(['name'], ['user_id'])(['Freshly Fresh Herrmann'], [42]) // update entry's name where user_id=42
94+
95+
UsersTable.delete(['user_id'])([1]) // delete where user_id=1
96+
UsersTable.delete(['user_id'])(['abcd']) // compiler error, since user_id has type number
97+
98+
// create custom query using a join
99+
const query = SQL.raw<typeof Tables.users & typeof Tables.user_emails>(`
100+
SELECT *
101+
FROM ${UsersTable.name} u
102+
INNER JOIN ${UserEMailsTable.name} e ON u.user_id = e.user_id_ref
103+
WHERE u.date_removed IS NULL
104+
AND u.user_id = $1;
105+
`, [42])
106+
```
107+
108+
### Database Client
109+
110+
`postgres-schema-builder` also provides a small database client to perform our typesafe and custom queries.
111+
112+
```typescript
113+
import { DatabaseClient } from "postgres-schema-builder"
114+
import { Pool } from "pg"
115+
import { UsersTable, Tables} from "./tables.ts"
116+
import { config } from "./some-config.ts"
117+
118+
const database = DatabaseClient(
119+
new Pool({
120+
host: config.database.host,
121+
port: config.database.port,
122+
user: config.database.user,
123+
password: config.database.password,
124+
database: config.database.database,
125+
})
126+
);
127+
128+
// single query statements
129+
await database.query(
130+
UsersTable.create()
131+
)
132+
await database.query(
133+
UsersTable.insertFromObj({
134+
name: 'Fresh Herrmann',
135+
date_added: new Date(),
136+
settings: { a: 42, b: 'no' },
137+
})
138+
)
139+
const dbResults = await database.query(UsersTable.selectAll('*'))
140+
141+
// batch queries
142+
const insertStatements = someDataArray.map(entry => UsersTable.insertFromObj(entry))
143+
144+
await database.batch(insertStatements)
145+
146+
// leverage transaction creating your database schema
147+
const createTableStatements = composeCreateTableStatements(Tables) // performs a topological sort on your tables defined in <Tables>
148+
149+
await database.transaction(async (client) => {
150+
createTableStatements.forEach(createTableStatement => client.query({ sql: createTableStatement }))
151+
});
152+
```
153+
154+
## Todos
155+
156+
* Improve and extend docs
157+
* Allow `insert` and `insertFromObj` returning the inserted data
158+
* Enable client to perform multiple queries
159+
* Introduce database management object enabling schema versioning with migrations
160+
* Extend test cases and improve code coverage
161+
162+
## Support
163+
164+
### Node.js
165+
Currently, this package is automatically tested under Node.js versions `8 - 13`.
166+
All build artifact are compiled to ES6.
167+
168+
### PostgreSQL
169+
Tested under `v9.6`, might work for newer versions as well.
170+
171+
## Contributors
172+
* Yannick Stachelscheid ([@yss14](https://github.com/yss14))
173+
174+
## License
175+
This project is licensed under the [MIT](LICENSE) license.

0 commit comments

Comments
 (0)