diff --git a/packages/common/package.json b/packages/common/package.json index 5924ef6..08da5df 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -9,5 +9,12 @@ "dev": "node -e \"console.log('dev not configured')\"", "lint": "node -e \"console.log('lint not configured')\"", "test": "node -e \"console.log('test not configured')\"" + }, + "dependencies": { + "@prisma/client": "^5.18.0", + "redis": "^4.7.0" + }, + "devDependencies": { + "prisma": "^5.18.0" } } diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma new file mode 100644 index 0000000..c3975a1 --- /dev/null +++ b/packages/common/prisma/schema.prisma @@ -0,0 +1,10 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +// TODO: Translate docs/architecture/postgres-schema.md into Prisma models. diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index cb0ff5c..03ece44 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1 +1,2 @@ -export {}; +export * from './infra/postgres'; +export * from './infra/redis'; diff --git a/packages/common/src/infra/postgres.ts b/packages/common/src/infra/postgres.ts new file mode 100644 index 0000000..461c325 --- /dev/null +++ b/packages/common/src/infra/postgres.ts @@ -0,0 +1,42 @@ +import { PrismaClient, type PrismaClientOptions } from '@prisma/client'; + +export interface PostgresConfig { + url: string; + log?: PrismaClientOptions['log']; +} + +export interface PostgresConnector { + readonly prisma: PrismaClient; + connect(): Promise; + disconnect(): Promise; +} + +export const resolvePostgresConfigFromEnv = ( + env: NodeJS.ProcessEnv = process.env +): PostgresConfig => { + const url = env.DATABASE_URL ?? ''; + if (!url) { + throw new Error('DATABASE_URL is required to create a Postgres client.'); + } + + return { url }; +}; + +export const createPostgresConnector = ( + config: PostgresConfig +): PostgresConnector => { + const prisma = new PrismaClient({ + datasources: { + db: { + url: config.url, + }, + }, + log: config.log, + }); + + return { + prisma, + connect: () => prisma.$connect(), + disconnect: () => prisma.$disconnect(), + }; +}; diff --git a/packages/common/src/infra/redis.ts b/packages/common/src/infra/redis.ts new file mode 100644 index 0000000..e5b6d6c --- /dev/null +++ b/packages/common/src/infra/redis.ts @@ -0,0 +1,43 @@ +import { createClient, type RedisClientOptions, type RedisClientType } from 'redis'; + +export interface RedisConfig { + url: string; + database?: number; + options?: Omit; +} + +export interface RedisConnector { + readonly client: RedisClientType; + connect(): Promise; + disconnect(): Promise; +} + +export const resolveRedisConfigFromEnv = ( + env: NodeJS.ProcessEnv = process.env +): RedisConfig => { + const url = env.REDIS_URL ?? ''; + if (!url) { + throw new Error('REDIS_URL is required to create a Redis client.'); + } + + const database = env.REDIS_DB !== undefined ? Number(env.REDIS_DB) : undefined; + if (database !== undefined && Number.isNaN(database)) { + throw new Error('REDIS_DB must be a number when provided.'); + } + + return { url, database }; +}; + +export const createRedisConnector = (config: RedisConfig): RedisConnector => { + const client = createClient({ + url: config.url, + database: config.database, + ...config.options, + }); + + return { + client, + connect: () => client.connect(), + disconnect: () => client.quit(), + }; +};