•  


GitHub - tim-smart/sqlfx: A SQL toolkit for Effect-TS
Skip to content

tim-smart/sqlfx

Repository files navigation

sqlfx

A SQL toolkit for Effect-TS

https://tim-smart.github.io/sqlfx

Basic example

import
 {
 pipe
 }
 from
 "effect/Function"

import
 *
 as
 Config
 from
 "effect/Config"

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


const
 PgLive
 =
 Pg
.
makeLayer
(
{

  database
: 
Config
.
succeed
(
"effect_pg_dev"
)
,

}
)


const
 program
 =
 Effect
.
gen
(
function
*
 (
_
)
 {

  const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


  const
 people
 =
 yield
*
 _
(

    sql
<
{

      readonly
 id
: 
number

      readonly
 name
: 
string

    }
>
`SELECT id, name FROM people`
,

  )


  yield
*
 _
(
Effect
.
log
(
`Got 
${
people
.
length
}
 results!`
)
)

}
)


pipe
(
program
,
 Effect
.
provideLayer
(
PgLive
)
,
 Effect
.
runPromise
)

INSERT resolver

import
 {
 pipe
 }
 from
 "effect/Function"

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Schema
 from
 "@effect/schema/Schema"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


class
 Person
 extends
 Schema
.
class
(
{

  id
: 
Schema
.
number
,

  name
: 
Schema
.
string
,

  createdAt
: 
Schema
.
DateFromSelf
,

  updatedAt
: 
Schema
.
DateFromSelf
,

}
)
 {
}


const
 InsertPersonSchema
 =
 pipe
(

  Person
.
schemaStruct
(
)
,

  Schema
.
omit
(
"id"
,
 "createdAt"
,
 "updatedAt"
)
,

)


export
 const
 makePersonService
 =
 Effect
.
gen
(
function
*
 (
_
)
 {

  const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


  const
 insert
 =
 sql
.
resolver
(

    "InsertPerson"
,

    InsertPersonSchema
,

    Person
.
schema
(
)
,

    requests
 =>

      sql
`

        INSERT INTO people

        ${
sql
.
insert
(
requests
)
}

        RETURNING people.*

      `
,

  )
.
execute


  return
 {
 insert 
}

}
)

SELECT resolver

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Schema
 from
 "@effect/schema/Schema"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


class
 Person
 extends
 Schema
.
Class
(
{

  id
: 
Schema
.
number
,

  name
: 
Schema
.
string
,

  createdAt
: 
Schema
.
DateFromSelf
,

  updatedAt
: 
Schema
.
DateFromSelf
,

}
)
 {
}


export
 const
 makePersonService
 =
 Effect
.
gen
(
function
*
 (
_
)
 {

  const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


  const
 getByIdResolver
 =
 sql
.
idResolver
(

    "GetPersonById"
,

    Schema
.
number
,

    Person
.
schema
(
)
,

    _
 =>
 _
.
id
,

    ids
 =>
 sql
`SELECT * FROM people WHERE id IN 
${
sql
(
ids
)
}
`
,

  )


  const
 getById
 =
 (
id
: 
number
)
 =>

    Effect
.
withRequestCaching
(
"on"
)
(
getByIdResolver
.
execute
(
id
)
)


  return
 {
 getById 
}

}
)

Building queries

Safe interpolation

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


export
 const
 make
 =
 (
limit
: 
number
)
 =>

  Effect
.
gen
(
function
*
 (
_
)
 {

    const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


    const
 statement
 =
 sql
`SELECT * FROM people LIMIT 
${
limit
}
`

    // e.g. SELECT * FROM people LIMIT ?

  }
)

Unsafe interpolation

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


type
 OrderBy
 =
 "id"
 |
 "created_at"
 |
 "updated_at"

type
 SortOrder
 =
 "ASC"
 |
 "DESC"


export
 const
 make
 =
 (
orderBy
: 
OrderBy
,
 sortOrder
: 
SortOrder
)
 =>

  Effect
.
gen
(
function
*
 (
_
)
 {

    const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


    const
 statement
 =
 sql
`SELECT * FROM people ORDER BY 
${
sql
(
orderBy
)
}
 ${
sql
.
unsafe
(
sortOrder
)
}
`

    // e.g. SELECT * FROM people ORDER BY `id` ASC

  }
)

Where clause combinators

AND

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


export
 const
 make
 =
 (
names
: 
string
[
]
,
 cursor
: 
string
)
 =>

  Effect
.
gen
(
function
*
 (
_
)
 {

    const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


    const
 statement
 =
 sql
`SELECT * FROM people WHERE 
${
sql
.
and
(
[

      sql
`name IN 
${
sql
(
names
)
}
`
,

      sql
`created_at < 
${
sql
(
cursor
)
}
`
,

    ]
)
}
`

    // SELECT * FROM people WHERE (name IN ? AND created_at < ?)

  }
)

OR

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


export
 const
 make
 =
 (
names
: 
string
[
]
,
 cursor
: 
Date
)
 =>

  Effect
.
gen
(
function
*
 (
_
)
 {

    const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


    const
 statement
 =
 sql
`SELECT * FROM people WHERE 
${
sql
.
or
(
[

      sql
`name IN 
${
sql
(
names
)
}
`
,

      sql
`created_at < 
${
sql
(
cursor
)
}
`
,

    ]
)
}
`

    // SELECT * FROM people WHERE (name IN ? OR created_at < ?)

  }
)

Mixed

import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


export
 const
 make
 =
 (
names
: 
string
[
]
,
 afterCursor
: 
Date
,
 beforeCursor
: 
Date
)
 =>

  Effect
.
gen
(
function
*
 (
_
)
 {

    const
 sql
 =
 yield
*
 _
(
Pg
.
tag
)


    const
 statement
 =
 sql
`SELECT * FROM people WHERE 
${
sql
.
or
(
[

      sql
`name IN 
${
sql
(
names
)
}
`
,

      sql
.
and
(
[

        `created_at >
${
sql
(
afterCursor
)
}
`
,

        `created_at < 
${
sql
(
beforeCursor
)
}
`
,

      ]
)
,

    ]
)
}
`

    // SELECT * FROM people WHERE (name IN ? OR (created_at > ? AND created_at < ?))

  }
)

Migrations

A Migrator module is provided, for running migrations.

Migrations are forward-only, and are written in Typescript as Effect's.

Here is an example migration:

// src/migrations/0001_add_users.ts


import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"


export
 default
 Effect
.
flatMap
(

  Pg
.
tag
,

  sql
 =>
 sql
`

    CREATE TABLE users (

      id serial PRIMARY KEY,

      name varchar(255) NOT NULL,

      created_at TIMESTAMP NOT NULL DEFAULT NOW(),

      updated_at TIMESTAMP NOT NULL DEFAULT NOW()

    )

  `
,

)

To run your migrations:

// src/main.ts


import
 *
 as
 Effect
 from
 "effect/Effect"

import
 *
 as
 Pg
 from
 "@sqlfx/pg"

import
 *
 as
 Migrator
 from
 "@sqlfx/pg/Migrator"

import
 *
 as
 Config
 from
 "effect/Config"

import
 {
 fileURLToPath
 }
 from
 "node:url"

import
 *
 as
 Layer
 from
 "effect/Layer"

import
 {
 pipe
 }
 from
 "effect/Function"


const
 program
 =
 Effect
.
gen
(
function
*
 (
_
)
 {

  // ...

}
)


const
 PgLive
 =
 Pg
.
makeLayer
(
{

  database
: 
Config
.
succeed
(
"example_database"
)
,

}
)


const
 MigratorLive
 =
 Layer
.
provide
(

  Migrator
.
makeLayer
(
{

    directory
: 
fileURLToPath
(
new
 URL
(
"migrations"
,
 import
.
meta
.
url
)
)
,

    // Where to put the `_schema.sql` file

    schemaDirectory
: 
"src/migrations"
,

  }
)
,

  PgLive
,

)


const
 EnvLive
 =
 Layer
.
mergeAll
(
PgLive
,
 MigratorLive
)


pipe
(

  program
,

  Effect
.
provideLayer
(
EnvLive
)
,

  Effect
.
tapErrorCause
(
Effect
.
logErrorCause
)
,

  Effect
.
runFork
,

)
- "漢字路" 한글한자자동변환 서비스는 교육부 고전문헌국역지원사업의 지원으로 구축되었습니다.
- "漢字路" 한글한자자동변환 서비스는 전통문화연구회 "울산대학교한국어처리연구실 옥철영(IT융합전공)교수팀"에서 개발한 한글한자자동변환기를 바탕하여 지속적으로 공동 연구 개발하고 있는 서비스입니다.
- 현재 고유명사(인명, 지명등)을 비롯한 여러 변환오류가 있으며 이를 해결하고자 많은 연구 개발을 진행하고자 하고 있습니다. 이를 인지하시고 다른 곳에서 인용시 한자 변환 결과를 한번 더 검토하시고 사용해 주시기 바랍니다.
- 변환오류 및 건의,문의사항은 juntong@juntong.or.kr로 메일로 보내주시면 감사하겠습니다. .
Copyright ⓒ 2020 By '전통문화연구회(傳統文化硏究會)' All Rights reserved.
 한국   대만   중국   일본