Giter Site home page Giter Site logo

kepelrs / nestjs-prisma-crud Goto Github PK

View Code? Open in Web Editor NEW
116.0 3.0 9.0 7.99 MB

CRUD package for NestJS and Prisma ORM

Home Page: https://kepelrs.github.io/nestjs-prisma-crud

License: MIT License

JavaScript 0.44% TypeScript 99.56%
nestjs nest prisma2 prisma crud scaffold scaffolding typescript rest-api

nestjs-prisma-crud's Introduction

nestjs-prisma-crud

CRUD utility for simple REST use cases. Builds on top of NestJS and Prisma. Inspired by @nestjsx/crud.

Installation

npm i nestjs-prisma-crud --save
npm i nestjs-prisma-crud-schematics --save-dev

Why

When building REST API's there is common functionality that we would prefer not to implement again and again. This package offers minimal and opinionated out of the box solutions for some of those (see features below).

✔ Features

An overview of the provided functionality:

  1. Advanced client side joining, sorting, filtering and pagination via query parameters
    • Any valid prisma .where can be sent by the frontend.
    • Server side validation to safeguard against arbitrarily deep .join or .where clauses by clients.
    • Support for including only specific properties in the response.
  2. Access control
    • @AccessPolicy decorator with default utilities that support functionalities similar to RBAC/ABAC.
    • Custom policy support.
  3. Atomic operations
    • Supports POST/PATCH with nested objects.
    • Transaction support when extending controller functionality.
  4. Schematics
    • crud-resource: a modified NestJS resource schematic that scaffolds the entire CRUD module for you.
      One-line scaffolding with: nest g -c nestjs-prisma-crud-schematics crud-resource <YOUR-TABLE-NAME-HERE>
  5. Plug and play
    • Can be used alongside your other non nestjs-prisma-crud controllers.
    • You can still use PrismaCrudService and @AccessPolicy in your custom controllers if you want to retain some of nestjs-prisma-crud's functionalities.

Quickstart

Assumes both NestJS and Prisma2 are already setup. See Getting Started.

  1. Install nestjs-prisma-crud and nestjs-prisma-crud-schematics:

    npm i nestjs-prisma-crud
    npm i nestjs-prisma-crud-schematics --save-dev
    
  2. Generate the entire crud module with a single command (replace post with your model's name):

    nest g -c nestjs-prisma-crud-schematics crud-resource post
    
  3. Setup your service and controller

    // post.service.ts
    import { Injectable } from '@nestjs/common';
    import { PrismaCrudService } from 'nestjs-prisma-crud';
    
    @Injectable()
    export class PostService extends PrismaCrudService {
        constructor() {
            super({
                model: 'post',
                allowedJoins: ['comments.author'],
            });
        }
    }
    
    // post.controller.ts
    import { PostService } from './post.service';
    
    @Controller('post')
    export class PostController {
        constructor(private readonly postService: PostService) {}
    
        @Get()
        async findMany(@Query('crudQuery') crudQuery: string) {
            const matches = await this.postService.findMany(crudQuery);
            return matches;
        }
    }

Documentation

Go here for full documentation.

License

MIT licensed

Built with

TSDX
np
yarn 1.22.10

nestjs-prisma-crud's People

Contributors

kepelrs avatar renovate-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

nestjs-prisma-crud's Issues

Array Columns fail under assumption they are relations

For a model with a column that's an array:

model Example {
  tags String[]
}

This library will assume this is a relation and the requests will fail.

My proposed solution would be to either infer that this is not a relation using the prisma schema, or to access an array of keys in PrismaService that will pass through those keys exactly as given in the PATCH request.

@kepelrs let me know which method you'd prefer and I'll open a PR.

could you make dto generate skipable

the dtos generated by nestjs-prisma-crud has no field read from prisma.
so i used @vegardit/prisma-generator-nestjs-dto to generate dto and entities.
but crud will report an file exsting err.
could you make dto generation optional ?

Help: Two Register Modules in one Project

Hello, I am trying to access to two databases in the same project, I have already managed to configure prisma for the different schemes, but I cannot configure prismaCrudModule.register for both prisma Clients, is it possible to do this?

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Errored

These updates encountered an error and will be retried. Click on a checkbox below to force a retry now.

  • chore(deps): update dependency jest to v29.7.0
  • chore(deps): update typescript-eslint monorepo to v7.7.1 (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)

Other Branches

These updates are pending. To force PRs open, click the checkbox below.

  • chore(deps): update dependency ts-jest to v29.1.2

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/tests.yml
  • actions/checkout v4
  • actions/setup-node v4
npm
docs/package.json
  • @docusaurus/core 2.0.0-beta.15
  • @docusaurus/preset-classic 2.0.0-beta.15
  • @docusaurus/theme-search-algolia 2.0.0-beta.15
  • @mdx-js/react 1.6.22
  • @svgr/webpack 8.1.0
  • clsx 2.1.0
  • file-loader 6.2.0
  • prism-react-renderer 1.3.5
  • react 18.2.0
  • react-dom 18.2.0
  • url-loader 4.1.1
package.json
  • joi ^17.4.2
  • object-traversal ^1.0.0
  • @nestjs/common 10.3.8
  • @nestjs/core 10.3.8
  • @prisma/client 5.13.0
  • husky 9.0.11
  • prisma 5.13.0
  • rxjs 7.8.1
  • tsdx 0.14.1
  • tslib 2.6.2
  • typescript 5.4.5
  • @nestjs/common ^8.0.0 || ^9.0.0 || ^10.0.0
  • @nestjs/core ^8.0.0 || ^9.0.0 || ^10.0.0
  • @prisma/client ^3.0.2 || ^4.0.0 || ^5.0.0
  • rxjs ^7.3.0
  • node >=10
  • @typescript-eslint/eslint-plugin ^7.0.0
  • @typescript-eslint/parser ^7.0.0
  • jest ^29.0.0
  • ts-jest ^29.0.0
  • typescript 5.4.5
test/package.json
  • @nestjs/common 10.3.8
  • @nestjs/core 10.3.8
  • @nestjs/mapped-types 2.0.5
  • @nestjs/platform-express 10.3.8
  • @prisma/client 5.13.0
  • class-transformer 0.5.1
  • class-validator 0.14.1
  • object-traversal 1.0.1
  • rimraf 5.0.5
  • rxjs 7.8.1
  • @nestjs/cli 10.3.2
  • @nestjs/schematics 10.1.1
  • @nestjs/testing 10.3.8
  • @types/express 4.17.21
  • @types/jest 29.5.12
  • @types/node 20.12.7
  • @types/supertest 6.0.2
  • @typescript-eslint/eslint-plugin 7.7.1
  • @typescript-eslint/parser 7.7.1
  • eslint 8.57.0
  • eslint-config-prettier 9.1.0
  • eslint-plugin-prettier 5.1.3
  • jest 29.7.0
  • prettier 3.2.5
  • prisma 5.13.0
  • supertest 6.3.4
  • ts-jest 29.1.2
  • ts-loader 9.5.1
  • ts-node 10.9.2
  • tsconfig-paths 4.2.0
  • typescript 5.4.5
  • @typescript-eslint/eslint-plugin 7.7.1
  • @typescript-eslint/parser 7.7.1
  • jest 29.7.0
  • ts-jest 29.1.2
  • typescript 5.4.5

  • Check this box to trigger a request for Renovate to run again on this repository

Basic adoption for remix?

You think this could be ported to the remix.run framework and if so, do you have a license that allows this?

have been toying with the idea for quite some time.

[Feature] option to compare context instead of role hard code

It's common to compare usr.roles with accessRequired roles.
but i think it's more flexiable and convenient to compare usr.priviledge with controller name and method name.

why ?

  • if write accessRequired roles in decorator, it's hard code, not manageable on frontend.
  • if compare priviledges, it's more flexiable, because one user may has many roles, one role may has many priviledges.
  • if we use priviledges, this information is already there in controller. both class and handler method could be found by ctx. so we may use accessGuard on controller or gloabal.

example

in controller:

@accessGuard                // same as @accessGuard('CatsController.*')
@controller('cats')
class CatsController....
        create().....
        delete().....

in appModule.ts

@Module({
  imports: [
   
    PrismaCrudModule.register({
      accessControl: {
        compareTo: 'context',   // handler and class
        authDataKey: 'user',
        getRolesFromAuthDataFn: (authenticatedUser) => authenticatedUser?.priviledges,
        strict: false,
      },
    }),
  ]
})

user.priviledges:

//extracted from all roles:
[
  'CatsController.create',
  'someController.delete',
  '*.read',
  'some.*'
]
** it's better to support wildcard **

in accessGuard we compare user.priviledges With the context ( handler and class).

so we haven't to specify accessGuard on every api. also it's more flexiable to handle acess with complex situations. more convenient and flexiable.

Suggestion: pagination + take/skip + hasMore + cursor

It's a common implementation to use pager logic but I'm not super fond of it.

Actually, I don't mind handling skip=pageSize*page front end side.

One simple use case I dislike in page=2 logic is when user changes pageSize.
Trying to guess what page we should go to, and where in the page it'll find the records that where visible at time of page size change. (so easy to simply reuse the current skip)

On the other hand, something I really appreciate to have in the api response is a hasMore or endOfRecords boolean.

As I'm considering using this crud module on a personal project, I thought I could offer to contribute by first adding a few things on the roadmap (and hopefully give a hand).

Suggestions

  • Add a hasMore boolean in the response
  • Add the ability to use skip/take pagination logic
    • exposing take skip in all responses. (?)
    • if page and take/skip present in request throw error.
  • Take advantage of cursor logic
    • this would imply checking orderBy is on a unique key.

Implementing cursor logic would probably be to challenging for me...

Include on findOne

How can I use include to populate the foreign id field in findOne method?

Queries

Regardless of what I write in a query. The GET request ignores it and returns everything with the default parameters. This is the case with all types of input parameters. Any idea what is causing that?
Screen Shot 2022-06-14 at 8 05 56 PM

Use prisma generated type for `model` argument

Description

Hello again, I am suggesting that we use the interface exported by @prisma/client on built to pass into model: during constructor.

Motivation and Context

  1. Better type-safety
  2. Potentially allowing Swagger doc to show possible field

Examples

Instead of

@Injectable()
export class WebhookService extends PrismaCrudService {
  constructor() {
    super({
      model: 'webhook',
      allowedJoins: ['tenant', 'file'],
      defaultJoins: [],
    })
  }
}

We do

import { Webhook } from '@prisma/client'

@Injectable()
export class WebhookService extends PrismaCrudService {
  constructor() {
    super({
      model: Webhook,
      allowedJoins: ['tenant', 'file'], // e.g. Error will be thrown when a wrong field is passed in
      defaultJoins: [],
    })
  }
}

Reference

I've seen this being done in https://github.com/chax-at/prisma-filter that its @DirectFilterPipe will recognize the field types in the whitelist array.

_count queries don't work

Hey, when doing joins like this:

{
   "joins":[
      "clients",
      "_count.select.comments"
   ]
}

the library is parsing that as:

{
   "_count":{
      "include":{
         "select":{
            "include":{
               "comments":true
            }
         }
      }
   },
   "client":true
}

Which throws a prisma error. It should parse to:

 {
   "client":true,
   "_count":{
      "select":{
         "comments":true
      }
   }
}

Limit joins

There doesn't seem to be a way to limit joins. It will simply return all of the linked relations, even if there are thousands of them. It would be nice to be able to set a limit at least, effectively a pageSize. Even better if we can order them as well.

SyntaxError: Unexpected token a in JSON at position 1

I am getting thses or Undefined ('aggregate') errors and i also dont know how to findMany() without putting any query while using swagger and i am only getting error can you review this error please

[Nest] 96837 - 06/15/2023, 5:54:07 PM ERROR [ExceptionsHandler] Unexpected token a in JSON at position 1 SyntaxError: Unexpected token a in JSON at position 1 at JSON.parse () at PrismaQueryBuilder.parseCrudQuery (/Users/safalneupane/Desktop/Ongoing Projects/Inventory-Management-System/node_modules/nestjs-prisma-crud/src/crud/prisma-query-builder.ts:85:30) at tryCatch (/Users/safalneupane/Desktop/Ongoing Projects/Inventory-Management-System/node_modules/nestjs-prisma-crud/node_modules/regenerator-runtime/runtime.js:63:40) at new Promise ()

[Enhancement] Improve error handling with invalid query

Steps to reproduce

  1. Init project with flat model (no relations required) and expose getMany endpoint
  2. Send crudQ formatted request containing 'contains' operato:
	where: {
		someValidField: { contains: { notallowedFormat: 'but lets try' } }
       }

Current result

Server responds with 403 Forbidden which made me re-visit docs and config (I don't have any Access Policies setup). However it has nothing to do with them but with the invalid query.

Expected result

I would expect having a 500 or 400 server response due to the incorrect request being sent.

support more package manager or manually install

after execute this command , npm was used to install packages.
but , i'm using pnpm.
could you add support with it ? Or just let user install packages manually.

nest g -c nestjs-prisma-crud-schematics crud-resource post

Crud for model with JSON property not working (provided nested relation is not allowed)

Hi,

I'm trying out this CRUD generation service and hit a bit of a blocker:

This is my simple prisma model:

model Label {
  id           String   @id @default(cuid())
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
  name         String
  data         Json
}

I've followed the steps to set up the crud controller and it automatically created a label module.
However, when I fire a request:

curl --request POST \
  --url http://localhost:3000/label \
  --header 'content-type: application/json' \
  --data '{
  "name": "Testing",
  "data": {
    "someprop": "somval"
  }
}'

It returns the following error:

{
  "message": "Provided nested relation is not allowed: data",
  "error": "Bad Request",
  "statusCode": 400
}

You can see it's complaining about the JSON object.
When I try to create a new label from PrismaClient directly, there is no problem.

Not sure if this is a bug or a security feature on the crud controller?

BUG: Many-to-many relationship fail on update

image
Let's just say that I have these relationship tables certificate----job_certification----job

Here is the payload PATCH request:

{
  "job_certification": [
    {
      "certification_id": 1
    }
  ],
}

But when I ran it, it'll error

  177 // Checks that entity is accessible considering id and crudQuery restrictions.
  178 const entity = await this.findOne(id, fullOpts);
  179
 180 await repo.update({
        where: {
          id: 1
        },
        data: {
          job_certification: {
            create: [
              {
                certification_id: 1
              }
            ],
            connect: undefined,
            disconnect: [
              {
                id: undefined
              }
            ]
            ~~~~~~~~~~~~~~~~~
          },
....
Argument `disconnect`: Invalid value provided. Expected job_certificationWhereUniqueInput, provided (Object).

function getIdsToDisconnect(
stillConnected: any[],
originalConnections: any[],
idPropertyName: string,
) {
const originalIds = (originalConnections || []).map((v) => v[idPropertyName]);
const stillConnectedSet = new Set(stillConnected.map((v) => v[idPropertyName]));
const forDisconnecting = [];
for (let i = 0; i < originalIds.length; i++) {
const id = originalIds[i];
if (!stillConnectedSet.has(id)) {
forDisconnecting.push(id);
}
}
return forDisconnecting.map((v) => ({ [idPropertyName]: v }));
}

Then I tried to debug from function getIdsToDisconnect

param originalConnections [ { job_id: 1, certification_id: 1 } ]
param stillConnected []
param idPropertyName id
originalIds [ undefined ]
stillConnectedSet Set(0) {}
forDisconnecting [ { id: undefined } ]

So, because I executed in job service, it will use the job's idPropertyName which is id.
But the many-to-many table is not using id as the column name, it will error

I guess the solution is to be able override idPropertyName just for many-to-many relationship

EDIT: Wrong Payload

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.