Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split server into read/write instances #14

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
SERVER_WRITE_PORT=3001
SERVER_READ_PORT=3002

SERVER_0_PORT=6969
SERVER_1_PORT=6970
SERVER_2_PORT=6971
Expand Down
11 changes: 9 additions & 2 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
"license": "MIT",
"scripts": {
"build": "nest build",
"start": "npm run prisma:deploy && npm run prisma:generate && npm run start:prod",
"test": "npm run prisma:reset && npm run prisma:deploy && npm run prisma:seed && npm run test:e2e",
"lint": "eslint './**/*.ts'",
"format": "eslint './**/*.ts' --fix",
"start:read": "npm run prisma:start && node dist/server/main.read.js",
"start:write": "npm run prisma:start && node dist/server/main.write.js",
"prisma:start": "npm run prisma:deploy && npm run prisma:generate",
"prisma:test": "npm run prisma:reset && npm run prisma:deploy && npm run prisma:seed",
"prisma:deploy": "npx prisma migrate deploy --schema ./prisma/random_db/schema.prisma",
"prisma:generate": "npx prisma generate --schema ./prisma/random_db/schema.prisma",
"prisma:migrate": "npx prisma migrate dev --schema ./prisma/random_db/schema.prisma --name migration --skip-seed",
"prisma:reset": "npx prisma migrate reset --schema ./prisma/random_db/schema.prisma --force --skip-seed",
"prisma:seed": "ts-node ./prisma/seed.ts",
"start:prod": "node dist/server/main.js",
"lint": "eslint './**/*.ts'",
"format": "eslint './**/*.ts' --fix",
"test": "npm run prisma:test && npm run test:e2e",
"test:e2e": "npx jest --verbose --runInBand"
},
"testRegex": ".*\\.spec\\.ts$",
Expand All @@ -37,7 +39,8 @@
"@nestjs/terminus": "^10.2.0",
"@prisma/client": "^5.8.1",
"axios": "1.7.4",
"class-validator": "^0.14.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"jest": "29.1.1",
"js-yaml": "^4.1.0",
"npm": "^10.5.2",
Expand Down
42 changes: 42 additions & 0 deletions app/server/api/abstract/abstract.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
Logger,
HttpException,
HttpStatus,
NotFoundException,
ConflictException,
applyDecorators,
} from '@nestjs/common'
import { ApiHeader } from '@nestjs/swagger'

const knownExceptions = new Set([NotFoundException, ConflictException])

export function ApiAuthHeaders() {
return applyDecorators(
ApiHeader({
name: 'username',
description: 'Username for authentication',
required: true,
}),
ApiHeader({
name: 'password',
description: 'Password for authentication',
required: true,
})
)
}

export abstract class AbstractController {
protected readonly logger = new Logger(AbstractController.name)

protected handleHttpException(error: any): Error {
this.logger.error(`Error processing request: ${error.message}`)
this.logger.verbose(error.stack)
if (!knownExceptions.has(error.constructor)) {
throw new HttpException(
`Internal Server Error: ${error.constructor.name} - ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR
)
}
return error
}
}
23 changes: 23 additions & 0 deletions app/server/api/abstract/abstract.database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Logger } from '@nestjs/common'

export abstract class AbstractDatabase<DatabaseMethods> {
protected readonly logger = new Logger(AbstractDatabase.name)

protected readonly dbClient: DatabaseMethods
protected readonly idKey: string

constructor(dbClient: DatabaseMethods, idKey: string) {
if (!dbClient) {
throw new Error(
'Prisma database client is undefined in AbstractReadDatabase constructor.'
)
}
if (!idKey) {
throw new Error(
'ID key is undefined in AbstractReadDatabase constructor.'
)
}
this.dbClient = dbClient
this.idKey = idKey
}
}
29 changes: 29 additions & 0 deletions app/server/api/abstract/abstract.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { DynamicModule, InjectionToken, Type } from '@nestjs/common'

interface GenericModuleOptions {
controller: Type<any>;
service: Type<any>;
serviceToken: InjectionToken;
dbImplementation: Type<any>;
dbToken: InjectionToken;
}

export class AbstractModule {
static configure(options: GenericModuleOptions): DynamicModule {
return {
module: AbstractModule,
controllers: [options.controller],
providers: [
{
provide: options.serviceToken,
useClass: options.service,
},
{
provide: options.dbToken,
useClass: options.dbImplementation,
},
],
exports: [options.serviceToken, options.dbToken],
}
}
}
85 changes: 0 additions & 85 deletions app/server/api/abstract/controller.ts

This file was deleted.

46 changes: 0 additions & 46 deletions app/server/api/abstract/module.ts

This file was deleted.

14 changes: 14 additions & 0 deletions app/server/api/abstract/read.controller.abstract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AbstractController } from './abstract.controller'
import { AbstractReadService } from './read.service.abstract'

export abstract class AbstractReadController<T> extends AbstractController {
protected readonly service: AbstractReadService<T>

constructor(service: AbstractReadService<T>) {
super()
this.service = service
}

abstract findById(id: string): Promise<T>;
abstract findAll(): Promise<T[]> ;
}
25 changes: 25 additions & 0 deletions app/server/api/abstract/read.database.abstract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NotFoundException } from '@nestjs/common'
import { AbstractDatabase } from './abstract.database'

interface ReadDatabaseMethods {
findUnique: (params: any) => Promise<any>;
findMany: () => Promise<any[]>;
}

export abstract class AbstractReadDatabase<T> extends AbstractDatabase<ReadDatabaseMethods> {
async findById(id: string): Promise<T> {
const existingRecord = await this.dbClient.findUnique({
where: {
[this.idKey]: id,
},
})
if (!existingRecord) {
throw new NotFoundException(`Record with ID ${id} not found.`)
}
return existingRecord
}

async findAll(): Promise<T[]> {
return await this.dbClient.findMany()
}
}
20 changes: 20 additions & 0 deletions app/server/api/abstract/read.service.abstract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Logger } from '@nestjs/common'
import { AbstractReadDatabase } from './read.database.abstract'

export abstract class AbstractReadService<T> {
protected readonly logger = new Logger(AbstractReadService.name)

protected readonly database: AbstractReadDatabase<T>

constructor(database: AbstractReadDatabase<T>) {
this.database = database
}

async findById(id: string): Promise<T> {
return await this.database.findById(id)
}

async findAll(): Promise<T[]> {
return await this.database.findAll()
}
}
30 changes: 0 additions & 30 deletions app/server/api/abstract/service.ts

This file was deleted.

Loading
Loading