Skip to content
Merged
18 changes: 16 additions & 2 deletions pg.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
const sql = require('./sql')

const sqlPG = db => async (...args) => {
module.exports = sql

module.exports.query = db => (...args) => db.query(sql(...args))

module.exports.one = db => async (...args) => {
const {
rows: [row]
} = await db.query(sql(...args))
return row
}

module.exports.many = db => async (...args) => {
const { rows } = await db.query(sql(...args))
return rows
}

module.exports = sqlPG
module.exports.count = db => async (...args) => {
const { rowCount } = await db.query(sql(...args))
return rowCount
}
24 changes: 15 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,31 @@ const main = async () => {

#### Shorthand for postgres

Since we ❤️ [node-postgres](https://github.com/brianc/node-postgres) so much, we created a shorthand for it :
Since we ❤️ [node-postgres](https://github.com/brianc/node-postgres) so much, we created shorthands and helpers for it :

```js
// long-version
const sql = require('@sequencework/sql')
const { rows: movies } = await db.query(sql`select * from movies`)

// equivalent, short-version
const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sql/pg
const movies = await sql(db)`select * from movies`
// sql(db) just calls db.query so db can be a client or a pool :)

// main export stays the same
const query = sql`select * from movies where id = ${id}`

// default pg result object : https://node-postgres.com/api/result
const { rows, rowCount } = await sql.query(db)`select * from movies`

// helpers
const movies = await sql.many(db)`select * from movies`
const movie = await sql.one(db)`select * from movies where id = ${id}`
const nbMovie = await sql.count(
db
)`update from movies set name = ${name} where id = ${id}`
```

You can then rewrite the previous `listMoviesByYear` function in a much more concise way 😎

```js
const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sql/pg

const listMoviesByYear = async (db, yearRange) => sql(db)`
const listMoviesByYear = async (db, yearRange) => sql.many(db)`
select * from movies
where
year >= ${yearRange[0]}
Expand Down
36 changes: 26 additions & 10 deletions test/pg.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
const sqlPG = require('../pg')
const sql = require('../pg')

test('shorthand for node-postgres', async () => {
const sampleBooks = ['book1', 'book2']
const db = {
query: async ({ text, values }) => {
if (text === 'select * from books') {
return { rows: sampleBooks }
}
return { rows: [] }
const sampleBooks = ['book1', 'book2']
const db = {
query: async ({ text, values }) => {
if (text === 'select * from books') {
return { rows: sampleBooks, rowCount: sampleBooks.length }
}
return { rows: [], rowCount: 0 }
}
}

const books = await sqlPG(db)`select * from books`
test("sql.query should return pg's query result", async () => {
const { rows, rowCount } = await sql.query(db)`select * from books`
expect(rows).toBe(sampleBooks)
expect(rowCount).toBe(sampleBooks.length)
})

test('sql.one should return the first row', async () => {
const book = await sql.one(db)`select * from books`
expect(book).toBe(sampleBooks[0])
})

test('sql.many should return rows', async () => {
const books = await sql.many(db)`select * from books`
expect(books).toBe(sampleBooks)
})

test('sql.count should return rowCount', async () => {
const nbBooks = await sql.count(db)`select * from books`
expect(nbBooks).toBe(sampleBooks.length)
})
72 changes: 50 additions & 22 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,64 @@
// TypeScript Version: 2.9

declare namespace sqlElements {
interface QueryConfig {
_sql?: SqlContainer
text: string
values: any[]
}

class SqlContainer {
constructor(chains: ReadonlyArray<string>, expressions: any[])
readonly chains: ReadonlyArray<string>
readonly expressions: any[]
}
}

declare module '@sequencework/sql' {
function sql(
chains: ReadonlyArray<string>,
...expressions: any[]
): sqlElements.QueryConfig
): sql.QueryConfig

namespace sql {
interface QueryConfig {
_sql?: SqlContainer
text: string
values: any[]
}

class SqlContainer {
constructor(chains: ReadonlyArray<string>, expressions: any[])
readonly chains: ReadonlyArray<string>
readonly expressions: any[]
}
}

export = sql
}

declare module '@sequencework/sql/pg' {
function sqlPG(chains: {
readonly query: (
queryExpression: sqlElements.QueryConfig
) => Promise<{
import _sql = require('@sequencework/sql')

function sql(
chains: ReadonlyArray<string>,
...expressions: any[]
): sql.QueryConfig

namespace sql {
type QueryConfig = _sql.QueryConfig

interface PGQueryResult {
rowCount: number
rows: any[]
}>
}): (chains: ReadonlyArray<string>, ...expressions: any[]) => Promise<any[]>
}

interface queryable<T extends PGQueryResult = PGQueryResult> {
readonly query: (queryExpression: QueryConfig) => Promise<T>
}

function query<T extends PGQueryResult>(
db: queryable<T>
): (chains: ReadonlyArray<string>, ...expressions: any[]) => Promise<T>

export = sqlPG
function one(
chains: queryable
): (chains: ReadonlyArray<string>, ...expressions: any[]) => Promise<any>

function many(
chains: queryable
): (chains: ReadonlyArray<string>, ...expressions: any[]) => Promise<any[]>

function count(
chains: queryable
): (chains: ReadonlyArray<string>, ...expressions: any[]) => Promise<number>
}

export = sql
}
42 changes: 32 additions & 10 deletions types/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,39 @@ sql`

// shorthand for node-postgres
const sampleBooks = ['book1', 'book2']
const db = {
query: async ({ text, values }: { text: string; values: any[] }) => {
interface CustomQueryResult {
rows: any[]
rowCount: number
oid: number
}
const db: sqlPG.queryable<CustomQueryResult> = {
query: async ({ text, values }) => {
if (text === 'select * from books') {
return { rows: sampleBooks }
return {
rows: sampleBooks,
rowCount: sampleBooks.length,
oid: Math.random()
}
}
return { rows: [] }
return { rows: [], rowCount: 0, oid: Math.random() }
}
}
const getBooks = async (): Promise<string[]> => {
const rows = await sqlPG(db)`select * from books`
return rows as string[]
}
// $ExpectType Promise<string[]>
getBooks()
// sqlPG.query should return pg's query result
// $ExpectType Promise<CustomQueryResult>
sqlPG.query(db)`select * from books`

// sqlPG.one should return the first row
// $ExpectType Promise<any>
sqlPG.one(db)`select * from books`

// sqlPG.many should return rows
// $ExpectType Promise<any[]>
sqlPG.many(db)`select * from books`

// sqlPG.count should return rowCount
// $ExpectType Promise<number>
sqlPG.count(db)`select * from books`

// sqlPG should return PGQueryConfig
// $ExpectType QueryConfig
sqlPG`select * from books`