Skip to content
/ api_server_boilerplate Public template

typescript express board boilerplate using routing controller

License

Notifications You must be signed in to change notification settings

Q00/api_server_boilerplate

Repository files navigation

Welcome to api_server_boilerplate πŸ‘‹

Version Documentation License: MIT Cody Style: Google

easy to use typescript express boilerplate. You can use board api, user api, error tracking etc..


🏠 Homepage

Install

yarn install
# after put your env flie
yarn db:dev sync # development environment
# or
yarn db:sync # production environment

Usage

yarn dev # development environment
yarn start # production environment

Run tests

yarn prepare
yarn build
yarn test

Or you can use debug with vscode

code

model

There are base models. You can extend this base models.

Base model

export abstract class BaseModel {
  @IsInt()
  @Generated('increment')
  @PrimaryColumn({ type: 'bigint', transformer: [bigIntTransformer] })
  id!: number;

  @IsDate()
  @CreateDateColumn()
  createdAt!: Date;

  @IsDate()
  @UpdateDateColumn()
  updatedAt!: Date;

  @IsDate()
  @Column({ nullable: true, type: 'date', default: null })
  deletedAt?: Date | null;
}

Also There are base board, base comment model.

Base board model

// you can extends this class making child board and add user

export abstract class BaseBoard extends BaseModel {
  @Column({ length: 50 })
  @IsString()
  title!: string;

  @IsString()
  @Column({ type: 'text' })
  content!: string;

  @IsInt()
  @Column({ default: 0 })
  reportCount!: number;
}

Base comment model

// you can extends this class making child comment and add user

export abstract class BaseComment extends BaseModel {
  @Column({ length: 50 })
  @IsString()
  @MaxLength(50)
  comment!: string;

  @Column({ default: 0 })
  @IsInt()
  reportCount!: number;
}

service

Threr are base services. You can extend this base services to other child service.

Base service

// you can extends this BaseService to use common method

export abstract class BaseService<T extends BaseModel> {
  protected genericRepository: Repository<T>;
  private repo: ObjectType<T>;
  constructor(repo: ObjectType<T>) {
    this.genericRepository = getConnection().getRepository(repo);
    this.repo = repo;
  }
}

And then you just call super call with your using repository

  constructor() {
    super(RepoName);
  }

Base board service

@Service()
export abstract class BaseBoardService<T extends BaseBoard> extends BaseService<
  T
> {
  constructor(repo: ObjectType<T>) {
    super(repo);
  }
}

This service is base board service. In this service, there are common method about board. You can extend this service to other child board service.

Base comment service

export abstract class BaseCommentService<
  T extends BaseComment
> extends BaseService<T> {
  constructor(repo: ObjectType<T>) {
    super(repo);
  }
}

This service is base comment service. This service is very similar to base board service.

Provider

This module makes OAUTH logic. You can use base provider to extends other OAUTH.

export abstract class BaseProvider {
  protected accessToken: string;
  protected instance: AxiosInstance | null;
  constructor() {
    this.accessToken = '';
    this.instance = null;
  }
  setToken(accessToken: string) {
    this.accessToken = accessToken;
  }

  setInstance(url: string, headers: object) {
    this.instance = apiClient(url, headers);
    this.instance.interceptors.response.use(
      (response) => response,
      (err) => Promise.reject(err),
    );
  }

  getInstance() {
    return this.instance;
  }

  async generateToken(userId: number) {
    return `Bearer ${Authentication.generateToken(userId)}`;
  }
}

Auth Conroller use this provider to make JWT token.

Controller

There are BaseAuthController, BaseCommentController and Other Controller. This project use routing-controllers and typedi. Thier README help you understand this architecture.

Base Auth Controller

export class BaseAuthController<T extends BaseProvider> extends BaseController {
  // this can be used in child class (ExampleAuthController)
  protected userAccountService: UserAccountService;
  protected userService: UserService;
  constructor(protected provider: T) {
    super();
    this.provider = provider;
    this.userAccountService = Container.get(UserAccountService);
    this.userService = Container.get(UserService);
  }
}

Base Comment Controller

export abstract class BaseCommentController<
  U extends BaseComment,
  T extends BaseCommentService<U>
> extends BaseController {
  protected service: T;
  constructor(service: T) {
    super();
    this.service = service;
  }
}

If you want to extends this controller, you should call super with service like below.

@JsonController('/example_board_comment')
export class ExampleBoardCommentController extends BaseCommentController<
  ExampleBoardComment,
  ExampleBoardCommentService
> {
  //this private service automaticaly injected by typedi
  constructor(private exampleBoardCommentService: ExampleBoardCommentService) {
    super(exampleBoardCommentService);
  }
}

DTO

To make request schema, this project use class-validator. This request schema will be shown in swagger ui or Redoc.

Interceptor

This module use routing-controllers interceptor

Middleware

This module use routing-controllers Middleware

Database

This project use typeorm and connect with Postgres.

Naming Strategy

using snake case.

export class NamingStrategy extends DefaultNamingStrategy {
  tableName(targetName: string, userSpecifiedName: string | undefined): string {
    return plural(snakeCase(userSpecifiedName || targetName));
  }

  relationName(propertyName: string): string {
    return snakeCase(propertyName);
  }

  columnName(propertyName: string, customName: string) {
    return snakeCase(customName || propertyName);
  }

  joinColumnName(relationName: string, referencedColumnName: string) {
    return snakeCase(`${relationName}_${referencedColumnName}`);
  }

  joinTableColumnName(
    tableName: string,
    propertyName: string,
    columnName: string,
  ) {
    return snakeCase(`${tableName}_${columnName || propertyName}`);
  }
}

config

const typeOrmConfig: PostgresConnectionOptions = {
  type: 'postgres',
  host: process.env.DB_HOST,
  namingStrategy: new NamingStrategy(),
  port: Number(process.env.DB_PORT),
  username: process.env.DB_USER,
  password: process.env.DB_PW,
  database: process.env.DATABASE,
  synchronize: false,
  logging: false,
  entities: [`${path.join(__dirname, '..', 'model')}/**.[tj]s`],
  migrations: [`${path.join(__dirname, '..', 'model')}/migration/**.[tj]s`],
};

Env variable

DB_HOST=
DB_USER=
DB_PW=
PORT= # your server port
DB_PORT=
DATABASE= # database name
TEST_TOKEN= # jwt token to use in testing
SENTRY_DSN= # sentry dsn

Author

πŸ‘€ Q00 jqyu.lee@gmail.com

🀝 Contributing

Contributions, issues and feature requests are welcome!
Feel free to check issues page.
If you want to contribute this repo, check contribute page

πŸ” Relase note && Change log

Release note and change log are exist in CHANGELOG

Show your support

Give a ⭐️ if this project helped you!


This README was generated with ❀️ by readme-md-generator