1import * as Data from "effect/Data";
2import type * as Effect from "effect/Effect";
3import type * as Redacted from "effect/Redacted";
4import * as Binding from "../Binding.ts";
5import type { RuntimeContext } from "../RuntimeContext.ts";
6import type { MySQL } from "./MySQL.ts";
7
8/**
9 * Bind a {@link MySQL} service to a Railway {@link Service} and obtain
10 * the Effect-native connection string for `Drizzle.MySQL` /
11 * `SQL.MySQL`.
12 *
13 * `ConnectMySQL` is the Context tag, the type, and the callable —
14 * `yield* Railway.ConnectMySQL(Db)`. Provide {@link ConnectMySQLHttp}.
15 *
16 * **Example:** Bind MySQL in a Service
17 * ```typescript
18 * import * as Drizzle from "alchemy/Drizzle/MySQL";
19 *
20 * const conn = yield* Railway.ConnectMySQL(Db);
21 * const db = yield* Drizzle.MySQL(conn.connectionString);
22 *
23 * fetch: Effect.gen(function* () {
24 * const rows = yield* db.execute("select 1 as ok", "objects");
25 * });
26 * ```
27 *
28 * ### Variable references
29 * `ConnectMySQL` packs a typed private URI onto the Service. To store
30 * Railway's template instead of a resolved URI (IaC `db.env.MYSQL_URL`),
31 * pass `Railway.ref(Db, "MYSQL_URL")` as a {@link Variable} `value` or
32 * `Service.env` entry.
33 *
34 * **Example:** Railway.ref
35 * ```typescript
36 * const db = yield* Railway.MySQL("Db", { project: site });
37 * yield* Railway.Variable("MysqlUrl", {
38 * project: site,
39 * service: api,
40 * name: "MYSQL_URL",
41 * value: Railway.ref(db, "MYSQL_URL"),
42 * });
43 * ```
44 *
45 * @binding
46 * @product MySQL
47 * @category Storage & Databases
48 */
49export interface ConnectMySQL extends Binding.Service<
50 ConnectMySQL,
51 "Railway.ConnectMySQL",
52 (mysql: MySQL) => Effect.Effect<ConnectMySQLClient>
53> {}
54
55export const ConnectMySQL = Binding.Service<ConnectMySQL>(
56 "Railway.ConnectMySQL",
57);
58
59export const connectEnvKeys = (mysql: Pick<MySQL, "LogicalId">) => {
60 const id = mysql.LogicalId.replaceAll(/[^a-zA-Z0-9]/g, "_").toUpperCase();
61 return {
62 pooled: `RAILWAY_MYSQL_${id}_POOLED`,
63 direct: `RAILWAY_MYSQL_${id}_DIRECT`,
64 };
65};
66
67export class MySQLUrlMissing extends Data.TaggedError(
68 "Railway.MySQLUrlMissing",
69)<{
70 name: string;
71}> {}
72
73export interface ConnectMySQLClient {
74 /**
75 * Private (`{name}.railway.internal`) connection string. Pass this to
76 * {@link Drizzle.MySQL} or `SQL.MySQL` from a {@link Service}.
77 */
78 connectionString: Effect.Effect<
79 Redacted.Redacted<string>,
80 MySQLUrlMissing,
81 RuntimeContext
82 >;
83 /**
84 * Same private URI — Railway MySQL has no proxy-pooler split. Kept so
85 * callers matching the `ConnectPostgres` shape keep working.
86 */
87 directConnectionString: Effect.Effect<
88 Redacted.Redacted<string>,
89 MySQLUrlMissing,
90 RuntimeContext
91 >;
92}