Giter VIP home page Giter VIP logo

sqlfx's Introduction

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 }
})

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,
)

sqlfx's People

Contributors

arekmaz avatar datner avatar fubhy avatar github-actions[bot] avatar mattiamanzati avatar tiborkr avatar tim-smart avatar vecerek avatar wmaurer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

sqlfx's Issues

Migrator assumes running as a ESModule

The migrator assumes that it can use import here: https://github.com/tim-smart/sqlfx/blob/main/packages/sql/src/Migrator/Node.ts#L32
This makes the migrator fail in projects that haven't migrated over to ESM yet.

This also breaks on Windows where the following code breaks loading:

const MigratorLive = Layer.provide(
	PgLive,
	Migrator.makeLayer({
		loader: Migrator.fromDisk(fileURLToPath(new URL('migrations', import.meta.url))),
		schemaDirectory: 'src/migrations'
	})
);

which fails with:

node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[Error: {"reason":"import-error","message":"Could not import migration \"1_init\"\n\nError [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'","_tag":"MigrationError"}] {
  toJSON: [Function (anonymous)],
  toString: [Function (anonymous)],
  [Symbol(effect/Runtime/FiberFailure)]: Symbol(effect/Runtime/FiberFailure),
  [Symbol(effect/Runtime/FiberFailure/Cause)]: {
    _tag: 'Parallel',
    left: { _tag: 'Empty' },
    right: {
      _tag: 'Fail',
      error: {
        reason: 'import-error',
        message: 'Could not import migration "1_init"\n' +
          '\n' +
          "Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'",
        _tag: 'MigrationError'
      }
    }
  },
  [Symbol(nodejs.util.inspect.custom)]: [Function (anonymous)]
}

Related: nodejs/node#31710 nodejs/node#34765

Oracle support

Oracle has free docker containers and now a js only client as well :D would be nice to have some oracle driver support.

Add sql query logger

It would be useful to have a logger (aspect) that logs the sql query string sent to the db. Especially for being able to track how requests are being batched or when using sqlfx with kysely.

Migration error with node ESM module

I get this error in a Node project that compiles to ESM modules

Exited failure state: Error: {"reason":"import-error","message":"Could not import migration \"1_create_people\"\n\nError [ERR_REQUIRE_ESM]: require() of ES Module /Users/corentin/Dev/temp/use-relative-time/dist/src/migrations/0001_create_people.js from /Users/corentin/Dev/temp/use-relative-time/node_modules/.pnpm/@[email protected]_@[email protected][email protected]/node_modules/@sqlfx/sql/Migrator/Node/dist/sqlfx-sql-Migrator-Node.cjs.dev.js not supported.\nInstead change the require of 0001_create_people.js in /Users/corentin/Dev/temp/use-relative-time/node_modules/.pnpm/@[email protected]_@[email protected][email protected]/node_modules/@sqlfx/sql/Migrator/Node/dist/sqlfx-sql-Migrator-Node.cjs.dev.js to a dynamic import() which is available in all CommonJS modules.","_tag":"MigrationError"}

I made a reproduction repo available at https://github.com/tatchi/sqlx-repro

pnpm i
pnpm run build
node dist/src/main.js

It works if we compile to CommonJS. You can test with common branch

git checkout common
pnpm run build
node dist/src/main.js

Passing buffer values to `sql` produces invalid queries

Given:

const Args = Schema.struct({
  accountId: Schema.number,
  // ULIDFromBuffer: Schema.Schema<Buffer, ULID>
  ids: Schema.array(ULIDFromBuffer),
});

export type Args = Schema.To<typeof Args>;

export const query = (
  args: Args
) =>
  pipe(
    Sql.tag,
    Effect.bindTo("sql"),
    Effect.bind("input", () => Schema.encode(Args)(args)),
    Effect.map(
      ({ input, sql }) =>
        sql`SELECT * FROM my_events WHERE account_id = ${
          input.accountId
        } AND aggregate_id IN ${sql(input.ids)}`
    )
  );

I would expect the produced SQL query to be something of the following shape:

SELECT * FROM my_events WHERE account_id = ? AND aggregate_id IN (?)

However, in reality, we're getting:

SELECT * FROM my_events WHERE account_id = ? AND aggregate_id IN (`0`,`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`,`10`,`11`,`12`,`13`,`14`,`15`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)

A ulid, when encoded as a binary, is 16 bytes long (explains 0...15).

For more details, see Discord: https://discord.com/channels/795981131316985866/1148264145314127913

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.