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

[FEAT] Prisma support #1389

Closed
9 tasks done
Romakita opened this issue May 11, 2021 · 6 comments · Fixed by #1797
Closed
9 tasks done

[FEAT] Prisma support #1389

Romakita opened this issue May 11, 2021 · 6 comments · Fixed by #1797

Comments

@Romakita
Copy link
Collaborator

Romakita commented May 11, 2021

Information

Add Prisma framework support. Prisma has is own models to configure the table and relations. The idea is to provide a generator embed to the new package @tsed/prisma and generate classes and enums based on the schema.prisma configuration. The generated classes/enums could be used with the Controllers to generate the Swagger documentation.

Sponsor this feature: https://github.com/sponsors/Romakita?frequency=one-time&sponsor=Romakita

Currently balance: 15/15 one-time

Capture d’écran 2021-05-22 à 09 47 13

Roadmap

Example

Prisma configuration:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider        = "prisma-client-js"
}

generator tsed {
  provider = "@tsedio/prisma"
}

model User {
  /// @TsED.Groups("!creation")
  /// Comment
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  /// @TsED.Email()
  /// @TsED.Description("User email. This email must be unique!")
  email       String   @unique
  weight      Float?
  is18        Boolean?
  name        String?
  successorId Int?
  successor   User?    @relation("BlogOwnerHistory", fields: [successorId], references: [id])
  predecessor User?    @relation("BlogOwnerHistory")
  role        Role     @default(USER)
  posts       Post[]
  keywords    String[]
  biography   Json
}

model Post {
  id     Int   @id @default(autoincrement())
  user   User? @relation(fields: [userId], references: [id])
  userId Int?
}

enum Role {
  USER
  ADMIN
}

Then run the command to generates files:

npx prisma generate

Capture d’écran 2021-05-11 à 08 09 40

Outputs:

import { User } from "../client";
import { Integer, Required, Property, Groups, Format, Email, Description, Allow, Enum, CollectionOf } from "@tsed/schema";
import { Role } from "../enums";
import { PostModel } from "./PostModel";

export class UserModel implements User {
  @Property(Number)
  @Integer()
  @Required()
  @Groups("!creation")
  id: number;

  @Property(Date)
  @Format("date-time")
  @Required()
  createdAt: Date;

  @Property(String)
  @Required()
  @Email()
  @Description("User email. This email must be unique!")
  email: string;

  @Property(Number)
  @Allow(null)
  weight: number | null;

  @Property(Boolean)
  @Allow(null)
  is18: boolean | null;

  @Property(String)
  @Allow(null)
  name: string | null;

  @Property(Number)
  @Integer()
  @Allow(null)
  successorId: number | null;

  @Property(() => UserModel)
  @Allow(null)
  predecessor: UserModel | null;

  @Required()
  @Enum(Role)
  role: Role;

  @CollectionOf(() => PostModel)
  @Required()
  posts: PostModel[];

  @CollectionOf(String)
  @Required()
  keywords: string[];

  @Property(Object)
  @Required()
  biography: any;
}

And, the following repository:

import { isArray } from "@tsed/core";
import { deserialize } from "@tsed/json-mapper";
import { Injectable, Inject } from "@tsed/di";
import { PrismaService } from "../services/PrismaService";
import { Prisma, User } from "../client";
import { UserModel } from "../models";

@Injectable()
export class UsersRepository {
  @Inject()
  protected prisma: PrismaService;

  get collection() {
    return this.prisma.user
  }

  get groupBy() {
    return this.collection.groupBy.bind(this.collection)
  }

  protected deserialize<T>(obj: null | User | User[]): T {
    return deserialize<T>(obj, { type: UserModel, collectionType: isArray(obj) ? Array : undefined })
  }

  async findUnique(args: Prisma.UserFindUniqueArgs): Promise<UserModel | null> {
    const obj = await this.collection.findUnique(args);
    return this.deserialize<UserModel | null>(obj);
  }

  async findFirst(args: Prisma.UserFindFirstArgs): Promise<UserModel | null> {
    const obj = await this.collection.findFirst(args);
    return this.deserialize<UserModel | null>(obj);
  }

  async findMany(args?: Prisma.UserFindManyArgs): Promise<UserModel[]> {
    const obj = await this.collection.findMany(args);
    return this.deserialize<UserModel[]>(obj);
  }

  async create(args: Prisma.UserCreateArgs): Promise<UserModel> {
    const obj = await this.collection.create(args);
    return this.deserialize<UserModel>(obj);
  }

  async update(args: Prisma.UserUpdateArgs): Promise<UserModel> {
    const obj = await this.collection.update(args);
    return this.deserialize<UserModel>(obj);
  }

  async upsert(args: Prisma.UserUpsertArgs): Promise<UserModel> {
    const obj = await this.collection.upsert(args);
    return this.deserialize<UserModel>(obj);
  }

  async delete(args: Prisma.UserDeleteArgs): Promise<UserModel> {
    const obj = await this.collection.delete(args);
    return this.deserialize<UserModel>(obj);
  }

  async deleteMany(args: Prisma.UserDeleteManyArgs) {
    return this.collection.deleteMany(args)
  }

  async updateMany(args: Prisma.UserUpdateManyArgs) {
    return this.collection.updateMany(args)
  }

  async aggregate(args: Prisma.UserAggregateArgs) {
    return this.collection.aggregate(args)
  }
}

Usage:

import {BodyParams, Controller, Get, Post} from "@tsed/common";
import {Inject} from "@tsed/di";
import {Groups, Returns, Summary} from "@tsed/schema";
import {UserModel, UsersRepository} from "@tsedio/prisma";

@Controller("/users")
export class UsersController {
  @Inject()
  protected service: UsersRepository;

  @Post("/")
  @Summary("Create a new user")
  @Returns(201, UserModel)
  async signupUser(@BodyParams() @Groups("creation") user: UserModel): Promise<UserModel> {
    return this.service.create({data: user});
  }

  @Get("/")
  @Summary("Filter posts by title or content")
  @(Returns(200, Array).Of(UserModel).Description("Return a list of User"))
  getAll() {
    return this.service.findMany();
  }
}

Acceptance criteria

  • The package generate the classes and enums
  • The classes / enums can be imported from @tsed/prisma
  • Ts.ED decorators like Groups can be used in the schema.prisma
  • Documentation is available on tsed.io
  • The sponsor goal is reach ! (15 one-time sponsors or 5 monthly sponsors, see capture above). Your help is welcome!
@Romakita Romakita self-assigned this May 11, 2021
@Romakita Romakita pinned this issue May 11, 2021
@Romakita
Copy link
Collaborator Author

Romakita commented May 20, 2021

Results:
Capture d’écran 2021-05-23 à 16 52 11

@Romakita
Copy link
Collaborator Author

Support of Repositories generation is ready:
Capture d’écran 2021-05-23 à 15 08 43

@Romakita
Copy link
Collaborator Author

Capture d’écran 2021-05-23 à 16 05 43

@Romakita
Copy link
Collaborator Author

Romakita commented Mar 5, 2022

The goal is reached. Prisma module will be released in open-source!!

Thanks to all sponsors! Sponsors will keep his advantage on the support priority on this plugin ;)

Romakita added a commit that referenced this issue Mar 5, 2022
Romakita added a commit that referenced this issue Mar 5, 2022
Romakita added a commit that referenced this issue Mar 5, 2022
Romakita added a commit that referenced this issue Mar 5, 2022
Romakita added a commit that referenced this issue Mar 5, 2022
@Romakita
Copy link
Collaborator Author

Romakita commented Mar 5, 2022

🎉 This issue has been resolved in version 6.104.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Romakita Romakita unpinned this issue Mar 18, 2022
@Romakita
Copy link
Collaborator Author

🎉 This issue has been resolved in version 7.0.0-beta.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

1 participant