Leverage Prisma's auto-generated types as the input type for functions

Exploring the capabilities of Prisma ORM has led me to experiment with creating models and generating the PrismaClient. Initially, I thought it would be possible to utilize the generated types for variables and response types, but that doesn't seem to be the case.

...

model Invite {
  id        Int        @id @default(autoincrement())
  gender    Gender
  firstName String     @map("first_name")
  lastName  String     @map("last_name")
  language  Language
  email     String     @unique
  token     String     @unique

  roleId Int  @map("role_id")
  role   Role @relation(fields: [roleId], references: [id])

  organisationId Int          @map("organisation_id")
  organisation   Organisation @relation(fields: [organisationId], references: [id])

  updatedAt DateTime @updatedAt @map("updated_at")
  createdAt DateTime @default(now()) @map("created_at")

  @@map("invites")
}

...

The Invite model above includes relationships with the organisation and role models. In an invite service file, I'm preparing all the necessary input to create a new invite.

import { Request, Response, NextFunction } from 'express';
import roleRepository from '../repositories/role.repository';
import organisationRepository from '../repositories/organisation.repository';
import generateToken from '../utils/helpers/generateToken';
import inviteRepository from '../repositories/invite.repository';

const createInvite = async (req: Request, res: Response) => {
    const { body } = await validateInput(req);
    const { gender, firstName, lastName, email, language, roleId, organisationId } = body;

    const role = roleRepository.findRoleById(roleId);

    if (!role) {
        throw new Error('Role does not exist');
    }

    const organisation = organisationRepository.findOrganisationById(organisationId);

    if (!organisation) {
        throw new Error('Organisation does not exist');
    }

    const token = generateToken();

    const invite = inviteRepository.createInvite({
        gender,
        firstName,
        lastName,
        email,
        language,
        role: role,
        organisation: organisation,
        token,
    });

    await mailService.sendInviteEmail(invite);

    res.status(201).json({
        message: 'Invite created successfully',
    });
};

Within the createInvite function in the inviteRepository file, I attempt to use a generated Prisma type called InviteCreateInput.

const createInvite = async (inviteData: Prisma.InviteCreateInput): Promise<Invite> => {
    return db.invite.create({
        data: {
            gender: inviteData.gender,
            firstName: inviteData.firstName,
            lastName: inviteData.lastName,
            language: inviteData.language,
            email: inviteData.email,
            token: inviteData.token,
            role: {
                connect: {
                    id: inviteData.role
                }
            },
            organisation: {
                connect: {
                    id: inviteData.organisation,
                }
            }
        },
    });
};

Prisma suggests using this 'safe' Input approach, however, the InviteCreateInput type structure is not fitting for my needs.

  export type InviteCreateInput = {
    gender: Gender
    firstName: string
    lastName: string
    language: string
    email: string
    token: string
    updatedAt?: Date | string
    createdAt?: Date | string
    role: RoleCreateNestedOneWithoutInviteInput
    organisation: OrganisationCreateNestedOneWithoutInvitesInput
  }

The

RoleCreateNestedOneWithoutInviteInput
looks like this:

  export type RoleCreateNestedOneWithoutInviteInput = {
    create?: XOR<RoleCreateWithoutInviteInput, RoleUncheckedCreateWithoutInviteInput>
    connectOrCreate?: RoleCreateOrConnectWithoutInviteInput
    connect?: RoleWhereUniqueInput
  }

Considering the limitations of the Prisma generated types, I question whether to continue using them as function parameter types or to create custom types instead.

Answer №1

Prisma suggests that their approach is "safer" because they assume you might overlook checking for the roleId and organisationId (as done with findRoleById and findOrganisationById).

To optimize performance, consider eliminating the extra queries - findRoleById and findOrganisationById, and modify your middleware as follows:

const createInvite = async (req: Request, res: Response) => {
  const { body } = await validateInput(req);
  const { gender, firstName, lastName, email, language, roleId, organisationId } = body;

  const token = generateToken();

  try {
    const invite = await inviteRepository.createInvite({
      gender,
      firstName,
      lastName,
      email,
      language,
      token,
      role: {
        connect: {
          id: roleId,
        },
      },
      organisation: {
        connect: {
          id: organisationId,
        },
      },
    });
  } catch (error) {
    // handle errors if organisationId or roleId do not exist
    // use next(Error) or similar method
  }

  await mailService.sendInviteEmail(invite);

  res.status(201).json({
    message: 'Invite created successfully',
  });
};

You can also streamline your createInvite function:

const createInvite = async (data: Prisma.InviteCreateInput): Promise<Invite> => {
  return db.invite.create({
    data,
  });
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Resolving TypeScript error when importing images statically in Next.js

In order to enhance the performance of images in my nextjs app, I am working on image optimization. However, I encountered an issue stating: Cannot find module '/images/homeBg.jpg' or its corresponding type declarations. The image is actually st ...

Learn how to set up browser targeting using differential loading in Angular 10, specifically for es2016 or newer versions

Seeking advice on JS target output for compiled Angular when utilizing differential loading. By default, Angular compiles TypeScript down to ES5 and ES2015, with browsers using either depending on their capabilities. In order to stay current, I've b ...

Utilizing enum values in the HTML value attribute with Angular 2

I'm attempting to utilize an enum value in order to set the selected value of an HTML attribute: export enum MyEnum { FirstValue, SecondValue } export function MyEnumAware(constructor: Function) { constructor.prototype.MyEnum = MyEnum; } ...

The attribute 'pixiOverlay' is not found in the property

Working on my Angular 8 project, I needed to display several markers on a map, so I chose to utilize Leaflet. Since there were potentially thousands of markers involved, I opted for Leaflet.PixiOverlay to ensure smooth performance. After installing and imp ...

When attempting to open an Angular modal window that contains a Radio Button group, an error may occur with the message "ExpressionChanged

I am brand new to Angular and have been trying to grasp the concept of lifecycle hooks, but it seems like I'm missing something. In my current project, there is a Radio Button Group nested inside a modal window. This modal is triggered by a button cl ...

What is the mechanism behind making a Promise appear synchronous when using a Proxy in JavaScript?

const handler: ProxyHandler<any> = { get: (target: Promise<any>, prop: string, receiver: any) => { return target.then((o) => { return o[prop].apply(o); }); }, }; return new Proxy(obj, handler) ...

Having trouble injecting ActivatedRouteSnapshot into the component

Struggling to inject ActivatedRouteSnapshot into a component, encountering errors when trying to access query params. Here is the error stack trace: "Error: Can't resolve all parameters for ActivatedRouteSnapshot: (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?). a ...

Angular authentication guard does not redirect properly after returning a Promise<UrlTree>

My authentication guard is set up to control access to the /sign-in and /verify-email routes, allowing only users who are not logged in or have not verified their email: export const signInGuard: CanActivateFn = (activatedRouteSnapshot: ActivatedRouteSnap ...

Creating an interface in Dart: Step-by-step guide to defining interfaces similar to TypeScript

Coming from a Typescript background, I used to define object interfaces like this: export interface Locale { login: { title: string; actions: { submit: string; forgot: string; } } } However, in Dart, interfaces are implicit an ...

How can a TypeScript Angular directive utilize a function?

Recently, I have been following a unique Angular directive TypeScript pattern that I find really effective. The approach involves giving the directive its own isolated scope by creating a new controller. I encountered a scenario where I needed to invoke a ...

Struggling with configuring internationalization in NestJS

Currently, I am working on a NestJS project where my lead assigned me the task of returning different errors to the frontend based on the language in the request header. After some research, I decided to use i18n for this purpose. However, when testing it ...

Utilize the power of generics with Angular's service providers

Is it possible to make the membervar of class Parent generic instead of type any, while still retaining the ability to switch provider classes without having to update all components that rely on class Parent? For example, if class ChildB implements a diff ...

Angular: Implementing asynchronous data retrieval for templates

I am currently facing the following issue: I need to create a table with entries (Obj). Some of these entries have a file attribute. When an entry has a file attribute (entry.file), I need to make a backend call to retrieve the URL of that file: public g ...

File handling in Angular 2 using Typescript involves understanding the fundamental syntax for managing files

Would someone be able to explain the fundamental syntax for reading and writing text files, also known as file handling in TypeScript? If there is a corresponding link that anyone could provide, it would be greatly appreciated. ...

Reactive Programming: Transforming an earlier value as it moves down the pipeline

In a recent project, I encountered an interesting scenario involving the execution of multiple requests in a pipe chain. This specific case revolves around the display of images within the quill text editor. The backend returns the content in the followin ...

What is the best way to retrieve a specific field from the observable data stream?

When working with observables, I often find myself using them like this: ... const id = 1337; this.service.getThing(id).subscribe( suc => doSomething(suc.name), err = doSomethingElse() ); Lately, I've been utilizing the async pipe more freque ...

Transferring data between pages in Next JS using App Route and Typescript

Seeking assistance to extract data from an array on one page and display it on another page. I am working with NextJs, Typescript, and AppRoute. Code in app/page.tsx: import Image from 'next/image' import Link from 'next/link' const l ...

When using Next.js, I have found that the global.css file only applies styles successfully when the code is pasted directly into the page.tsx file. However, when attempting to

I recently started exploring nextjs and came across this video "https://www.youtube.com/watch?v=KzqNLDMSdMc&ab_channel=TheBraveCoders" This is when I realized that the CSS styles were not being applied to HeaderTop (the first component cre ...

Changes in the styles of one component can impact the appearance of other

When it comes to styling my login page, I have specific stylesheets that I include in login.component.ts. For all the common CSS files, I have added them in the root index ("index.html") using the traditional method. However, after a user logs into the sys ...

Typescript is failing to pick up process.env variables and is treating them as undefined, even though they are

I thought that by adding environment variables to environment.d.ts, they would have the correct types. I am using @types/node as a dev-dependency, and I have defined DATABASE_URL in my environment.d.ts like this: declare global { namespace NodeJS { ...