Git Product home page Git Product logo

prisma-client-js's Introduction

⚠️ We've moved our issue and feature tracking for the Prisma JS Client to prisma/prisma.

Rest assured, the Prisma JS Client is alive and well. We're just archiving this repository to make it more clear for everyone where to open issues. See you over at prisma/prisma!


Prisma Client JS: Query builder for TypeScript & Node.js (ORM replacement)

Quickstart   •   Website   •   Docs   •   API Reference   •   Examples   •   Blog   •   Slack   •   Twitter   •   Demo videos

Prisma Client JS is an auto-generated query builder that enables type-safe database access and reduces boilerplate. You can use it as an alternative to traditional ORMs such as Sequelize, TypeORM or SQL query builders like knex.js.

It is part of the Prisma ecosystem. Prisma provides database tools for data access, declarative data modeling, schema migrations and visual data management. Learn more in the main prisma repository or read the documentation.

Getting started

Follow one of these guides to get started with Prisma Client JS:

Alternatively you can explore the ready-to-run examples (REST, GraphQL, gRPC, plain JavaScript and TypeScript demos, ...) or watch the demo videos (1-2 min per video).

Contributing

Read more about how to contribute to Prisma Client JS here.

Security

If you have a security issue to report, please contact us at [email protected]

prisma-client-js's People

Contributors

0xflotus avatar aliedp avatar ctrlplusb avatar ejoebstl avatar errorname avatar ghashtag avatar gihrig avatar idkjs avatar janpio avatar jolg42 avatar kripod avatar kuldar avatar leonardopliski avatar matthewmueller avatar mfix22 avatar murajun1978 avatar neilime avatar nikolasburk avatar orzfly avatar pantharshit00 avatar prisma-bot avatar rafaelkr avatar schickling avatar steebchen avatar timsuchanek avatar tnunes avatar tomitrescak avatar trufa avatar wardpeet avatar weakky 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  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

prisma-client-js's Issues

Aggregations

type DynamicResult2 = (User & { aggregate: { age: { avg: number } } })[]
const dynamicResult2: DynamicResult2 = await prisma.users({
  select: { aggregate: { age: { avg: true } } },
})

type DynamicResult3 = User & {
  posts: (Post & { aggregate: { count: number } })[]
}
const dynamicResult3: DynamicResult3 = await prisma.users.findOne({
  where: 'bobs-id',
  select: { posts: { select: { aggregate: { count: true } } } },
})

const deletedCount: number = await prisma.users.deleteMany()

findOne returns Error when no record was found.

Yesterday I noticed the following bug:

let user = await context.photon.users.findOne({ where: { email: args.email } });

When the findOne function cannot find a record, it doesn't return null, which I believe would be the expected behaviour but instead it returns an error:

Screenshot 2019-07-04 at 08 35 25

Enable creation of record if not already present - `findOrCreate`

Problem

Whenever making use of database-stored configuration data, it is often wanted to check whether such data is available, and creating it if it’s not.
Doing that with the current client requires to add code which performs the check and creates the record if it’s not present.
Having a more concise way of performing these tasks would improve the overall developer experience.

Solution

Implement a findOrCreate function taking an object as input that performs the following:

  • looks for the input object using its primary key,
  • if not found, it goes ahead and creates it.

Split up `select` into `select` and `include`

Based on early user feedback for the new select API, I suggest the split up the select API into 2 parts:

  • select: { ... } explicitly select fields (by default empty selection set)
  • include: { ... } add fields to default selection set

Here's an example:

model Genre {
  id: String
  name: String
  popularity: Int
  tracks: Track[]
}

model Track {
  id: String
  name: String
  genre: Genre
}
// explicitly select some fields
const result = await photon.genres.findMany({
  first: 10,
  select: { id: true, name: true }
})

// all scalars and include all scalars of first 10 tracks
const result = await photon.genres.findMany({
  first: 10,
  include: {
    tracks: { first: 10 }
  }
})

Support for JSON/JSONB

Does Prisma Client support these types? If not, what are the plans to support them going forward?

ENOENT Error: Error in lift engine: [ Windows OS]

Execution of below command or any Lift related command is giving error in Windows (happening for most of the examples provided) No other logs provided except the below error message. Please help !

Command> prisma2 lift save --name 'init'
Error Message: ENOENT Error: Error in lift engine:

Cannot find module '@generated/photon'

when i use prisma2 init to generate a new project and run:

yarn run v1.17.0
$ ts-node prisma/seed.ts

/Users/a/Downloads/prisma2/node_modules/ts-node/src/index.ts:245
    return new TSError(diagnosticText, diagnosticCodes)
           ^
TSError: ⨯ Unable to compile TypeScript:
prisma/seed.ts:1:20 - error TS2307: Cannot find module '@generated/photon'.

1 import Photon from '@generated/photon'

@unique is not enforced with SQLite

I'm using this project file:

datasource db {
  provider = "sqlite"
  url      = "file:my.db"
}

generator nexus_prisma {
  provider = "nexus-prisma"
  output   = "node_modules/@generated/nexus-prisma"
}

generator photon {
  provider = "photonjs"
  output   = "node_modules/@generated/photon"
}

model User {
  id    String  @default(cuid()) @id
  email String  @unique
  name  String?
  posts Post[]
}
  
model Post {
  id        String   @default(cuid()) @id
  published Boolean
  title     String
  content   String?
  author    User?
}

Then I'm running this script:

import Photon from '@generated/photon'

const photon = new Photon()

async function main() {

  await photon.connect()

  const newUser = await  photon.users.create({
    data: { 
      email: '[email protected]', 
      name: 'Nikolas'
    },
    select: { id: true }
  })
  console.log(`Create a new user with ID: ${newUser.id}`)

  photon.disconnect()
}

main().catch(e => {
  console.error(e)
  photon.disconnect()
})

I can run it multiple times in a row and the DB happily creates new User records with the same email not enforcing the unique constraint that I specified on the email field.

image

Blacklist model names

Nonexhaustive List:

  • String
  • Int
  • Float
  • Subscription
  • DateTime
  • WhereInput
  • IDFilter
  • StringFilter

Inconsistent type generation

Are we treating the field name id somehow special? For model Blog { id Int @id } I don’t have the provide id in Photon but for model PostNews { new Int @id } I do have to provide the new field.

image
image

Loose connection with MySQL

When running a lot of operation at "the same time" against the database using a single instance of Photon, connections get refused.

Here a sample with debug: true

  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +0ms
  engine request to http://localhost:52108/status failed, reason: connect ECONNREFUSED 127.0.0.1:52108 +0ms
  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +58ms
  engine request to http://localhost:52108/status failed, reason: connect ECONNREFUSED 127.0.0.1:52108 +1ms
  engine request to http://localhost:52108/status failed, reason: connect ECONNREFUSED 127.0.0.1:52108 +57ms
  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +1ms
  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +54ms
  engine request to http://localhost:52108/status failed, reason: connect ECONNREFUSED 127.0.0.1:52108 +1ms
  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +78ms
  engine request to http://localhost:52108/status failed, reason: connect ECONNREFUSED 127.0.0.1:52108 +1ms
  engine request to http://localhost:52108/status failed, reason: connect ECONNREFUSED 127.0.0.1:52108 +54ms
  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +0ms
  engine request to http://localhost:52107/status failed, reason: connect ECONNREFUSED 127.0.0.1:52107 +57ms

Split up `select` into `select` and `include`

Based on early user feedback for the new select API, I suggest the split up the select API into 2 parts:

  • select: { ... } explicitly select fields (by default empty selection set)
  • include: { ... } add fields to default selection set

Here's an example:

model Genre {
  id: String
  name: String
  popularity: Int
  tracks: Track[]
}

model Track {
  id: String
  name: String
  genre: Genre
}
// explicitly select some fields
const result = await photon.genres.findMany({
  first: 10,
  select: { id: true, name: true }
})

// all scalars and include all scalars of first 10 tracks
const result = await photon.genres.findMany({
  first: 10,
  include: {
    tracks: { first: 10 }
  }
})

photonJs example where is prisma.yml

I'm following the instructions to a tee but it gets weird here

After having updated the datamodel, you need to deploy the changes:

prisma deploy

Note that this also invokes prisma generate (because of the post-deploy hook in prisma.yml) which regenerates the Prisma client in ./src/generated/prisma-client.

But i have no prisma.yml file?
I also don't have a /generated/prisma.graphlql, when were these meant to be created?

as this is prisma2 . should we not be using prisma2 deploy ??

Generated code has an absolute path

This line prints the cwd in generated code which is an absolute path.

This breaks the assumption that generated code (even in node_modules) can be moved around across machines (for developers or build systems).

Nested create is not working

Might be related to #30

Given this datamodel:

datasource db {
  provider = "sqlite"
  url      = "file:dev.db"
  default  = true
}

generator photon {
  provider = "photonjs"
}

model User {
  id       Int    @id
  username String @unique
  posts    Post[]
}

model Post {
  id   Int    @id
  data String
  user User
}

This mutation doesn't work:

 const testData = await photon.users.create({
    data: {
      username: 'harshit',
      posts: {
        create: {
          data: 'test',
        },
      },
    },
  })

Reproduction repo: https://github.com/pantharshit00/p2-nested-create

Enum from MySQL are undefined

Introspecting MySQL database works well, but when using a generated Photon client, if a column is an Enum, it will return undefined.

Where to put generated code

Problem

  • VSCode doesn't reload generated types when they are updated. While this problem is the most pressing for TS, this is also a problem for JS.

Strategies

Strategy A: VSCode Plugin

Approaches:

  • Override file exclusion for VSC (see this tweet)
  • VSC Plugin to hook into TS language server via an "embedded TS plugin" to tell TS server to reload on file changes

Notes:

  • Which files to look for should be derived from Prisma project file by collecting all output paths from all generators.

Strategy B: Combination of "hashed node_module" + generated entry point in user land

Notes:

  • We need a heuristic where to put the generated entrypoint file (e.g. by reading the rootDir value from tsconfig.json)
  • Just needed for TS version. For JS version we can directly import from node_modules.

Cons:

  • No convenience of non-relative imports (i.e. no more import Photon from '@prisma/photon')
  • Complicated to build
  • Requires to split things up into JS/TS generator version

Strategy C: Require user to add allowJS: true to tsconfig.json

Cons:

  • No longer supports tsc -d

TypeError: cb.apply is not a function

For some reason this copy breaks:

https://github.com/prisma/photon-js/blob/9507d5219ff2729a0a41cb3eef64382ec4d609c0/packages/photon/src/generation/generateClient.ts#L125

My guess is that when typescript compiles async/await into yield statements, fs-extra doesn't properly handle it.

Workaround (in transpiles code):

    yield fs_extra_1.default.copy(
      path_1.default.join(__dirname, '../../runtime'),
      path_1.default.join(outputDir, '/runtime'),
    )

to

    fs_extra_1.default.copySync(
      path_1.default.join(__dirname, '../../runtime'),
      path_1.default.join(outputDir, '/runtime'),
    )

I'd suggest we use: https://github.com/sindresorhus/cpy

Connection fails if module has been bundled via Parcel

Howdy!

First off—great work here. This project feels like a game changer. I'm so excited to be a part of it's evolution however possible.

I've been experimenting with using Photon in a monorepo, and using parcel to bundle certain pages for deployment out to lambdas. As such, there are some path changes and other things that get moved around a bit in the process.

When testing the package independently, Photon works as expected, however after the package contents get bundled into a singular file, it is no longer able to connect.

My entry point for the package consuming Photon returns a promise with the connected photon object:

index.ts

import Photon from '@generated/photon'
import { inspect } from 'util';

function initPhoton(photonConfig: any = {}) {
  const photon = new Photon(photonConfig);
  return photon.connect()
    .then(() => {
      console.log('photon connected');
      return photon;
    })
    .catch(error => {
      console.error('photon connection error', error);
      console.log(inspect(photon, { depth: 3, colors: true }));
      return false;
    });
}

export default initPhoton;
export { default as Photon } from '@generated/photon';

The file above is compiled via tsc and consumed by other packages within the project. I wrote a small test file within the same package to validate that it behaves as expected. This file simple imports the module as it is exported from the compiled artifact of the file above.

test.js

const initPhoton = require('./dist/index').default;

initPhoton()
  .then(photon => photon && photon.organizations.create({
    data: {
      access_token: '126', scope: '126', team_id: '126', team_name: '126', user_id: '126',
    },
  }))
  .then(console.log)
  .catch(console.error)
  .then(() => process.exit());

When I run the file above, via node test.js, it appears to execute as expected. Returning the entry as it is written to the database.

However, if I bundle the the test.js file using the command below, invoking the same file no longer works.

 npx parcel build test.js --target node --bundle-node-modules -d testDist

node testDist/test.js

I've been having trouble determining an actual source of the connection error. The connect() promise usually rejects with a value of 1 however if I copy the prisma folder into the testDist directory, the connectionPromise rejects with:

{ Error: spawn EACCES
    at ChildProcess.spawn (internal/child_process.js:313:11)
    at Object.exports.spawn (child_process.js:508:9)
    at t.default.start (/Users/eschoellhorn/Repos/praisely-slackbot/packages/praisely-data/testDist/test.js:50:110668)
    at y.start (/Users/eschoellhorn/Repos/praisely-slackbot/packages/praisely-data/testDist/test.js:50:21187)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:189:7)
    at Function.Module.runMain (module.js:696:11)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3 errno: 'EACCES', code: 'EACCES', syscall: 'spawn' }

I have experimented with trying to alter the cwd prop for the engine to point to an existing prisma folder but haven't been able to affect much meaningful change. Happy to keep trying, but could use some pointers...

Thanks and, again, awesome work!!

Some types are not working (DateTime and self declared ENUM on Postgres)

When introspecting my Postgres database, the schema is perfect. However, Photon will throw errors if types DateTime or some custom ENUM are present in the requested objects (even is those specific fields are not requested by graphql).

Commenting out the fields in the project.prisma and regenerating obviously makes the error go away.

Errors seems to come form the Rust library. Are these part of the early preview limitations ?

DateTime error below :

Error in Photonselect,cover: 
[
  {
    \"error\": \"ConnectorError(QueryError(Error { kind: FromSql, cause: Some(WrongType(Type(Timestamptz))) }\
\
stack backtrace:\
   0: failure::backtrace::internal::InternalBacktrace::new::h84e0252f893b7b0e (0x55a8a0489290)\
   1: failure::backtrace::Backtrace::new::h381a1eb507d04e2c (0x55a8a0489440)\
   2: <sql_connector::error::SqlError as core::convert::From<tokio_postgres::error::Error>>::from::h34ff4340a0dd5b3f (0x55a89febbc67)\
   3: sql_connector::database::postgresql::<impl sql_connector::row::ToSqlRow for tokio_postgres::row::Row>::to_sql_row::convert::h178249b965d8493a (0x55a89fe2686b)\
   4: sql_connector::database::postgresql::<impl sql_connector::row::ToSqlRow for tokio_postgres::row::Row>::to_sql_row::h3875436f09b0556f (0x55a89fe368ff)\
   5: sql_connector::database::postgresql::<impl sql_connector::transactional::Transaction for postgres::transaction::Transaction>::filter::h498f6550aa3967b1 (0x55a89fe9156a)\
   6: <sql_connector::database::postgresql::PostgreSql as sql_connector::transactional::Transactional>::with_transaction::hd5f1950fe91ab7e3 (0x55a89fbe22a8)\
   7: sql_connector::transactional::database_reader::<impl connector::database_reader::DatabaseReader for sql_connector::database::SqlDatabase<T>>::get_related_records::h291c7a1f45dc7434 (0x55a89fb52ab8)\
   8: core::executor::read::ReadQueryExecutor::execute_internal::hf955377e18499bf9 (0x55a89ff8d4fd)\
   9: core::executor::read::ReadQueryExecutor::execute_internal::hf955377e18499bf9 (0x55a89ff8f60c)\
  10: core::executor::Executor::exec_all::h019661aa466552b2 (0x55a89ffa4f55)\
  11: <prisma::req_handlers::graphql::GraphQlRequestHandler as prisma::req_handlers::RequestHandler>::handle::hd901880e604ba7f3 (0x55a89fc6494d)\
  12: prisma::http_handler::h574a96a3df5ad7b2 (0x55a89fc00499)\
  13: <F as actix_web::with::FnWith<T,R>>::call_with::h8921e8f777cf5423 (0x55a89fc5955e)\
  14: <actix_web::with::WithHandlerFut<T,S,R> as futures::future::Future>::poll::hd0405f55c326258c (0x55a89fc63271)\
  15: actix_web::pipeline::PipelineState<S,H>::poll::h05fe08bd6589e38c (0x55a89fb44610)\
  16: <actix_web::pipeline::Pipeline<S,H> as actix_web::server::handler::HttpHandlerTask>::poll_io::hf988cbba155aa2c2 (0x55a89fb33af0)\
  17: actix_web::server::h1::Http1Dispatcher<T,H>::poll_handler::h0e2de2e2f0e85c3b (0x55a89fb0afeb)\
  18: actix_web::server::h1::Http1Dispatcher<T,H>::poll::h7c512f9208c70f65 (0x55a89fbd73f7)\
  19: <actix_web::server::channel::HttpChannel<T,H> as futures::future::Future>::poll::h3fda956b624b7067 (0x55a89fbd39b9)\
  20: <actix_web::server::channel::HttpChannel<T,H> as futures::future::Future>::poll::h3fda956b624b7067 (0x55a89fbd48e6)\
  21: <actix_net::service::and_then::AndThenFuture<A,B> as futures::future::Future>::poll::hb1978ee7b706a005 (0x55a89fb3350d)\
  22: futures::future::chain::Chain<A,B,C>::poll::h4dcce436324a17dd (0x55a89fc7fe59)\
  23: futures::task_impl::std::set::hb6a916396ab13c6e (0x55a89fd4f262)\
  24: futures::task_impl::Spawn<T>::poll_future_notify::ha13909c8cd98d4fe (0x55a89fd69c66)\
  25: tokio_current_thread::CurrentRunner::set_spawn::hcab1a55dd9f5caaf (0x55a89fd49f74)\
  26: tokio_current_thread::scheduler::Scheduler<U>::tick::h1f8b6bf060538e24 (0x55a89fd566ca)\
  27: tokio_current_thread::Entered<P>::block_on::hd80cde5490457c04 (0x55a89fd4a92a)\
  28: std::thread::local::LocalKey<T>::with::h66e6fccf026a2b83 (0x55a89fd61138)\
  29: std::thread::local::LocalKey<T>::with::h7b6b9e8e24d2b1ae (0x55a89fd61f68)\
  30: std::thread::local::LocalKey<T>::with::h4efe62e0c4ca0fb4 (0x55a89fd6042f)\
  31: std::thread::local::LocalKey<T>::with::h1aefc504c0a5187d (0x55a89fd5f825)\
  32: tokio::runtime::current_thread::runtime::Runtime::block_on::h573894b7abc600ef (0x55a89fd4e180)\
  33: std::sys_common::backtrace::__rust_begin_short_backtrace::h027e6ea9ab4c2090 (0x55a89fd67687)\
  34: std::panicking::try::do_call::hbc22e41bfc445835 (0x55a89fd4c736)\
  35: __rust_maybe_catch_panic (0x55a8a04bce2a)\
             at src/libpanic_unwind/lib.rs:87\
  36: core::ops::function::FnOnce::call_once{{vtable.shim}}::hf39704692fc5ef53 (0x55a89fd67f26)\
  37: call_once<(),FnBox<()>> (0x55a8a04a325f)\
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702\
  38: call_once<(),alloc::boxed::Box<FnBox<()>>> (0x55a8a04bc240)\
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702\
      start_thread\
             at src/libstd/sys_common/thread.rs:14\
      thread_start\
             at src/libstd/sys/unix/thread.rs:80\
  39: start_thread (0x7f807e39b182)\
  40: __clone (0x7f807e2a8b1f)\
  41: <unknown> (0x0)))\"
  }
]

Enum error below (enum is a custom ROLES enum) :

Error in Photonselect,author: 
[
  {
    \"error\": \"ConnectorError(QueryError(Error { kind: FromSql, cause: Some(WrongType(Type(Other(Other { name: \\\"roles\\\", oid: 17089, kind: Enum([\\\"anonymous\\\", \\\"registered\\\", \\\"editor\\\", \\\"admin\\\", \\\"superadmin\\\"]), schema: \\\"public\\\" })))) }\
\
stack backtrace:\
   0: failure::backtrace::internal::InternalBacktrace::new::h84e0252f893b7b0e (0x5564cd836290)\
   1: failure::backtrace::Backtrace::new::h381a1eb507d04e2c (0x5564cd836440)\
   2: <sql_connector::error::SqlError as core::convert::From<tokio_postgres::error::Error>>::from::h34ff4340a0dd5b3f (0x5564cd268c67)\
   3: sql_connector::database::postgresql::<impl sql_connector::row::ToSqlRow for tokio_postgres::row::Row>::to_sql_row::convert::h178249b965d8493a (0x5564cd1d34cb)\
   4: sql_connector::database::postgresql::<impl sql_connector::row::ToSqlRow for tokio_postgres::row::Row>::to_sql_row::h3875436f09b0556f (0x5564cd1e38ff)\
   5: sql_connector::database::postgresql::<impl sql_connector::transactional::Transaction for postgres::transaction::Transaction>::filter::h498f6550aa3967b1 (0x5564cd23e56a)\
   6: <sql_connector::database::postgresql::PostgreSql as sql_connector::transactional::Transactional>::with_transaction::hd5f1950fe91ab7e3 (0x5564ccf8f2a8)\
   7: sql_connector::transactional::database_reader::<impl connector::database_reader::DatabaseReader for sql_connector::database::SqlDatabase<T>>::get_related_records::h291c7a1f45dc7434 (0x5564cceffab8)\
   8: core::executor::read::ReadQueryExecutor::execute_internal::hf955377e18499bf9 (0x5564cd33a4fd)\
   9: core::executor::read::ReadQueryExecutor::execute_internal::hf955377e18499bf9 (0x5564cd33c60c)\
  10: core::executor::Executor::exec_all::h019661aa466552b2 (0x5564cd351f55)\
  11: <prisma::req_handlers::graphql::GraphQlRequestHandler as prisma::req_handlers::RequestHandler>::handle::hd901880e604ba7f3 (0x5564cd01194d)\
  12: prisma::http_handler::h574a96a3df5ad7b2 (0x5564ccfad499)\
  13: <F as actix_web::with::FnWith<T,R>>::call_with::h8921e8f777cf5423 (0x5564cd00655e)\
  14: <actix_web::with::WithHandlerFut<T,S,R> as futures::future::Future>::poll::hd0405f55c326258c (0x5564cd010271)\
  15: actix_web::pipeline::PipelineState<S,H>::poll::h05fe08bd6589e38c (0x5564ccef1610)\
  16: <actix_web::pipeline::Pipeline<S,H> as actix_web::server::handler::HttpHandlerTask>::poll_io::hf988cbba155aa2c2 (0x5564ccee0af0)\
  17: actix_web::server::h1::Http1Dispatcher<T,H>::poll_handler::h0e2de2e2f0e85c3b (0x5564cceb7feb)\
  18: actix_web::server::h1::Http1Dispatcher<T,H>::poll::h7c512f9208c70f65 (0x5564ccf843f7)\
  19: <actix_web::server::channel::HttpChannel<T,H> as futures::future::Future>::poll::h3fda956b624b7067 (0x5564ccf809b9)\
  20: <actix_web::server::channel::HttpChannel<T,H> as futures::future::Future>::poll::h3fda956b624b7067 (0x5564ccf818e6)\
  21: <actix_net::service::and_then::AndThenFuture<A,B> as futures::future::Future>::poll::hb1978ee7b706a005 (0x5564ccee050d)\
  22: futures::future::chain::Chain<A,B,C>::poll::h4dcce436324a17dd (0x5564cd02ce59)\
  23: futures::task_impl::std::set::hb6a916396ab13c6e (0x5564cd0fc262)\
  24: futures::task_impl::Spawn<T>::poll_future_notify::ha13909c8cd98d4fe (0x5564cd116c66)\
  25: tokio_current_thread::CurrentRunner::set_spawn::hcab1a55dd9f5caaf (0x5564cd0f6f74)\
  26: tokio_current_thread::scheduler::Scheduler<U>::tick::h1f8b6bf060538e24 (0x5564cd1036ca)\
  27: tokio_current_thread::Entered<P>::block_on::hd80cde5490457c04 (0x5564cd0f792a)\
  28: std::thread::local::LocalKey<T>::with::h66e6fccf026a2b83 (0x5564cd10e138)\
  29: std::thread::local::LocalKey<T>::with::h7b6b9e8e24d2b1ae (0x5564cd10ef68)\
  30: std::thread::local::LocalKey<T>::with::h4efe62e0c4ca0fb4 (0x5564cd10d42f)\
  31: std::thread::local::LocalKey<T>::with::h1aefc504c0a5187d (0x5564cd10c825)\
  32: tokio::runtime::current_thread::runtime::Runtime::block_on::h573894b7abc600ef (0x5564cd0fb180)\
  33: std::sys_common::backtrace::__rust_begin_short_backtrace::h027e6ea9ab4c2090 (0x5564cd114687)\
  34: std::panicking::try::do_call::hbc22e41bfc445835 (0x5564cd0f9736)\
  35: __rust_maybe_catch_panic (0x5564cd869e2a)\
             at src/libpanic_unwind/lib.rs:87\
  36: core::ops::function::FnOnce::call_once{{vtable.shim}}::hf39704692fc5ef53 (0x5564cd114f26)\
  37: call_once<(),FnBox<()>> (0x5564cd85025f)\
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702\
  38: call_once<(),alloc::boxed::Box<FnBox<()>>> (0x5564cd869240)\
             at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702\
      start_thread\
             at src/libstd/sys_common/thread.rs:14\
      thread_start\
             at src/libstd/sys/unix/thread.rs:80\
  39: start_thread (0x7f0c57c0a182)\
  40: __clone (0x7f0c57b17b1f)\
  41: <unknown> (0x0)))\"
  }
]
`

Is pluralization still needed on `photon.<model_name>` ?

Description

Let's assume a User model.
Since we're planning to drop support for the shortcut photon.users({ ... }), I don't see the reason anymore to have pluralization.

It feels especially weird to me when doing photon.users.findOne({ ... })

Proposal

Remove pluralization and always use the model names lowercased.

photon.user.findOne()
photon.user.findMany()

Disallow returning data from delete operations

// Change from ...
const deletedUser: User = await prisma.users.delete('bobs-id')
// to ...
const deletedUser: User = await prisma.users.findOne('bobs-id')
const nothing: void = await prisma.users.delete('bobs-id')

Prisma Client JS apis for browser

It would be great If prisma2 can generate photon api for the browser. We can use Postgres row based permissions for security. The browser apis will allow developers to quickly build an application without using rest/graphql endpoint.

createdAt with default doesn't work

Datamodel

model Post {
  id Int @id
  createdAt DateTime  @default(now())
  updatedAt DateTime @updatedAt
  published Boolean @default(false)
  title String
  content String?
  author User
}

Photon (doesn't allow adding createdAt manually)

const result = await photon.posts.create({
    data: {
      title: title,
      content: content,
      author: { connect: { email: authorEmail } }
    }
  });

Query sent to query engine

  photon mutation {
  photon   createPost(data: {
  photon     title: "Title 1"
  photon     content: "Content 1"
  photon     author: {
  photon       connect: {
  photon         email: "[email protected]"
  photon       }
  photon     }                                                                                                                                                             photon   }) {
  photon     id                                                                                                                                                            photon     createdAt
  photon     updatedAt
  photon     published
  photon     title
  photon     content
  photon   }
  photon } +0ms

Route /filterPosts of rest-express example fails validation

A type-safe query fails at runtime validation

app.get('/filterPosts', async (req, res) => {
  const { searchString } = req.query
  const draftPosts = await photon.posts.findMany({
    where: {
      OR: [
        {
          title: {
            contains: searchString,
          },
        },
        {
          content: {
            contains: searchString,
          },
        },
      ],
    },
  })
  res.json(draftPosts)
})

Invalid `photon.posts.findMany()` invocation in /Users/divyendusingh/Documents/prisma/photon-js/examples/typescript/rest-express/src/index.ts:68:41

{
  where: {
    OR: {
      '0': {
        title: {
?         contains?: String,
?         equals?: String,
?         not?: String | StringFilter,
?         in?: String,                                                                                                                                                   ?         notIn?: String,
?         lt?: String,
?         lte?: String,
?         gt?: String,
?         gte?: String,
?         startsWith?: String,
?         endsWith?: String
        }
      },
      '1': {
        content: {
?         contains?: String,
?         equals?: String | null,
?         not?: String | null | NullableStringFilter,
?         in?: String,
?         notIn?: String,
?         lt?: String,
?         lte?: String,
?         gt?: String,
?         gte?: String,
?         startsWith?: String,
?         endsWith?: String
        }
      }
    }
  }
}

Life-cycle hooks

Middleware (blocking)

function beforeUserCreate(user: UserCreateProps): UserCreateProps {
  return {
    ...user,
    email: user.email.toLowerCase(),
  }
}

type UserCreateProps = { name: string }
type UserCreateCallback = (userStuff: UserCreateProps) => Promiselike<UserCreateProps>

const beforeUserCreateCallback: UserCreateCallback = user => ({
  name: 'Tim',
})

function afterUserCreate(user) {
  datadog.log(`User Created ${JSON.stringify(user)}`)
}

const prisma = new Prisma({
  middlewares: { beforeUserCreate, afterUserCreate },
})

Events (non-blocking)

const prisma = new Prisma()
prisma.on('User:beforeCreate', user => {
  stripe.createUser(user)
})

Group By

Problem

Most database support ways to indicate how to group results, especially in combination with usage of aggregators.
Prisma 2 currently doesn't offer any way to group results, and requires to fall back to using raw SQL queries. This defeats the benefits of having a type-safe client, and hurts code maintainability.

Suggested solution

Add support for ways to group results

Initial API example proposal:

type DynamicResult4 = {
  lastName: string
  records: User[]
  aggregate: { age: { avg: number } }
}
const groupByResult: DynamicResult4 = await prisma.users.groupBy({
  key: 'lastName',
  having: { age: { avgGt: 10 } },
  where: { isActive: true },
  first: 100,
  orderBy: { lastName: 'ASC' },
  select: {
    records: { take: 100 },
    aggregate: { age: { avg: true } },
  },
})

type DynamicResult5 = {
  raw: any
  records: User[]
  aggregate: { age: { avg: number } }
}
const groupByResult2: DynamicResult5 = await prisma.users.groupBy({
  raw: { key: 'firstName || lastName', having: 'AVG(age) > 50' },
  select: {
    records: { $first: 100 },
    aggregate: { age: { avg: true } },
  },
})

Next steps

  • Define scope of the kinds of group by statement we want to support first
  • Agree on API design
  • Implement in engines
  • Implement in client

Support for selecting relations of relations (and more sophisticated queries in general)

Perhaps I'm missing something in the documentation, but I can't find anything that describes how to select relations that are more than one layer deep. The example I found is this:

// The returned posts objects will have all scalar fields of the `Post` model and additionally all the categories for each post
const allPosts: Post[] = await photon.posts.findMany({
  include: ["categories"]
})

Okay, this gets me the categories on each returned post, but what if I want to also grab one or more relations of each returned category? I can't see any way to do that right now since include just takes a string array, not any kind of JSON object where i could specify what fields on each category I'd want returned (and iteratively go deeper if needed)

The GraphQL equivalent of what I'd like to be able to do is something like this:

{
 posts {
   id
   categories {
      id
      x {
        id
      } 
      y {
        id
      }
    }
 }
}

I stayed away from Prisma Client (and stuck with Bindings) precisely because it seemed to offer no support for these kinds of sophisticated queries; the fluid API was kind of neat for extremely simple use cases, but hopeless for anything beyond that. Is Photon limited in the same fashion?

I'd really like it if I could select data with Photon in a way similar to this:

// The returned posts objects will have all scalar fields of the `Post` model and additionally all the categories for each post
const allPosts: Post[] = await photon.posts.findMany({
  include: {
    id: true,
    categories: {
      id: true,
      x: {
        id: true
      },
      y: {
        id: true
      }
    }
  }
})

Generated typings are not working properly with JavaScript

Currently, you won't get proper typing unless you manually add .default to you photon instance.

const Photon = require('@generated/photon');
const photon = new Photon.default();

What I will suggest is also to export a named model Photon. Otherwise, even though Babel will transform .default interop exports but we will lose typing information using default exports. Named exports solve this completely.

Add `findOrNull` method

Using the demo project 'graphql-apollo-server' typescript project, I query on post and use a dummy id argument and expect to get a null post obj back, but instead I get an internal server error.

t.field('post', {
      type: 'Post',
      nullable: true,
      args: { id: idArg() },
      resolve: (parent, { id }, ctx) => {
        return ctx.photon.posts.findOne({
          where: {
            id,
          },
        })
      },
    })
Query:

{
 post(id: "123") {
   id
   author {
     id
   }
 }
}

Result: Error in Photon: \n[\n  {\n \"error\": \"ConnectorError(RecordDoesNotExist)\"\n  }\n]"

Is there a way to tell photon to just send back null or is this a bug?

`updateMany` should return count of updated records

I have this User model:

model User {
  id: Int @id
  name: String
}

The updateMany method for this model has the following signature:

 updateMany<T extends UserUpdateManyArgs>(args: Subset<T, UserUpdateManyArgs>): 'select' extends keyof T ? PromiseLike<UserGetPayload<ExtractUserUpdateManyArgsSelect<T>>> : UserClient<User>;

This means I'm just getting a single User object back. It should return the number records that have been updated in the database.

The same problem seems to exist for deleteMany.

image

Adjust default generation path to `@prisma/photon`

Currently the default output path for the generated Photon library is @generated/photon. While this is working well from a technical perspective, we've received the feedback that this might be potentially problematic as we don't own the @generated NPM organization.

A safer alternative would be @prisma/generated/photon or @prisma/photon.

More ideas welcome! 💡

Problem with the typescript auth demo project.

Using this example repo: https://github.com/prisma/photonjs/tree/master/examples/typescript/graphql-auth

I experience an error when I call the createDraft mutation.

Error: Cannot return null for non-nullable field Mutation.createDraft.
    at completeValue (/Users/sjensen/dev/prisma-simple-demo/node_modules/graphql/execution/execute.js:579:13)
    at /Users/sjensen/dev/prisma-simple-demo/node_modules/graphql/execution/execute.js:511:16
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

I found that I can fix it by adding an async await in the resolver but it doesn't make any sense to me why this would work.

Before:

  t.field('createDraft', {
      type: 'Post',
      args: {
        title: stringArg(),
        content: stringArg({ nullable: true })
      },
      resolve: (parent, { title, content }, ctx) => {
        const userId = getUserId(ctx);
        return  ctx.photon.posts.create({
          data: {
            title,
            content,
            published: false,
            author: { connect: { id: userId } }
          }
        });
      }
    });

After:
This fixes it- first awaiting the response, saving it as the variable draft and then returning that works just fine.

    t.field('createDraft', {
      type: 'Post',
      args: {
        title: stringArg(),
        content: stringArg({ nullable: true })
      },
      resolve: async (parent, { title, content }, ctx) => {
        const userId = getUserId(ctx);
        const draft = await ctx.photon.posts.create({
          data: {
            title,
            content,
            published: false,
            author: { connect: { id: userId } }
          }
        });
        return draft;
      }
    });

Alllow for array-based `select` syntax

Currently I can select fields in a Photon API call using the select syntax like so:

const allPosts: Post[] = await photon.posts.findMany({
  select: { id: true, author: true}
})

I think it would be great to provide an array-based API as well:

const allPosts: Post[] = await photon.posts.findMany({
  select: ["id", "author"]
})

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.