Skip to content

Instantly share code, notes, and snippets.

@arekmaz
Created October 27, 2023 14:59
Show Gist options
  • Select an option

  • Save arekmaz/6fdcf9e84e7f7a46172ce256e81a403b to your computer and use it in GitHub Desktop.

Select an option

Save arekmaz/6fdcf9e84e7f7a46172ce256e81a403b to your computer and use it in GitHub Desktop.

Revisions

  1. arekmaz created this gist Oct 27, 2023.
    109 changes: 109 additions & 0 deletions prisma-effect.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,109 @@
    import { db } from "../services/db.server";
    import { Data, Effect } from "effect";
    import type { PrismaClient } from "@prisma/client";
    import type {
    PrismaClientKnownRequestError,
    PrismaClientUnknownRequestError,
    PrismaClientRustPanicError,
    PrismaClientInitializationError,
    PrismaClientValidationError,
    } from "@prisma/client/runtime/library";

    export class PrismaError extends Data.TaggedError("PrismaError")<{
    details:
    | PrismaClientKnownRequestError
    | PrismaClientUnknownRequestError
    | PrismaClientRustPanicError
    | PrismaClientInitializationError
    | PrismaClientValidationError;
    }> {}

    type FilterNotContaining<
    Set,
    Needle extends string
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    > = Set extends `${infer _A}${Needle}${infer _B}` ? never : Set;

    type ExcludeFromUnionOtherTypes<From, E> = From extends E ? From : never;

    type ExcludeNonStringKeys<Obj> = {
    [k in ExcludeFromUnionOtherTypes<keyof Obj, string>]: k extends string
    ? Obj[k]
    : never;
    };

    type ExcludeKeysContaining<
    Obj extends Record<string, any>,
    Key extends string
    > = {
    [key in FilterNotContaining<keyof Obj, Key>]: Obj[key];
    };

    export type Client = ExcludeKeysContaining<
    ExcludeNonStringKeys<PrismaClient>,
    "$" | "_"
    > & {};

    type LazyPromiseToLazyEffect<Fn extends (...a: any[]) => any> = Fn extends (
    ...a: infer Args
    ) => Promise<infer Result>
    ? <R, E>(...a: Args) => Effect.Effect<R, E, Result>
    : never;

    type EffectifyObject<
    Obj extends Record<string, F>,
    F extends (...a: any[]) => any = any
    > = {
    [op in keyof Obj]: LazyPromiseToLazyEffect<Obj[op]>;
    };

    type EffectPrisma = {
    [model in keyof Client]: EffectifyObject<Client[model]>;
    };

    const createEffectClient = () => {
    return new Proxy(
    {},
    {
    get(_target, model) {
    return new Proxy(
    {},
    {
    get(_target, method) {
    return (...args: any[]) =>
    Effect.tryPromise(() => (db as any)[model][method](...args));
    },
    }
    );
    },
    }
    ) as EffectPrisma;
    };

    // example usage:

    // standard prisma:
    const a = await db.log.findMany();
    // a: {
    // id: number;
    // date: Date;
    // login: string | null;
    // info: string | null;
    // }[]

    // standard prisma:
    const e = createEffectClient().log.findMany();
    // Effect.Effect<unknown, unknown, {
    // id: number;
    // date: Date;
    // login: string | null;
    // info: string | null;
    // }[]>

    const e1 = createEffectClient().log.findMany<never, PrismaError>();
    // Effect.Effect<never, PrismaError, {
    // id: number;
    // date: Date;
    // login: string | null;
    // info: string | null;
    // }[]>