Skip to content

Commit

Permalink
tests: test & document 1:n relation projection (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt committed May 17, 2021
1 parent b40db97 commit afa09c6
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 55 deletions.
206 changes: 193 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@ Official Prisma plugin for Nexus.
- [Limitation: Hardcoded key for Prisma Client on GraphQL Context](#limitation-hardcoded-key-for-prisma-client-on-graphql-context)
- [Example: Exposing Prisma Client on GraphQL Context with Apollo Server](#example-exposing-prisma-client-on-graphql-context-with-apollo-server)
- [Project 1:1 Relation](#project-11-relation)
- [Example: Tests](#example-tests)
- [Example: Full 1:1](#example-full-11)
- [Limitation: Nullable on Without-Relation-Scalar Side](#limitation-nullable-on-without-relation-scalar-side)
- [Project 1:n Relation](#project-1n-relation)
- [Example: Tests](#example-tests-1)
- [Example: Full 1:n](#example-full-1n)
- [Prisma ID field to GraphQL ID scalar type mapping](#prisma-id-field-to-graphql-id-scalar-type-mapping)
- [Prisma Schema docs re-used for GraphQL schema doc](#prisma-schema-docs-re-used-for-graphql-schema-doc)
- [Prisma Schema docs re-used for JSDoc](#prisma-schema-docs-re-used-for-jsdoc)
- [Refined DX](#refined-dx)
- [Recipes](#recipes)
- [Project relation with custom resolver logic](#project-relation-with-custom-resolver-logic)
- [Supply custom custom scalars to your GraphQL schema](#supply-custom-custom-scalars-to-your-graphql-schema)
- [Notes](#notes)

Expand Down Expand Up @@ -97,15 +102,15 @@ export const schema = makeSchema({
- [x] ([#4](https://github.com/prisma/nexus-prisma/issues/4)) Support for Prisma Model field types that map to standard GraphQL scalars
- [x] ([#8](https://github.com/prisma/nexus-prisma/issues/8)) Support for Prisma Model field types of `DateTime` & `Json`
- [x] ([#16](https://github.com/prisma/nexus-prisma/issues/16)) Support for Prisma enums
- [x] ([#25](https://github.com/prisma/nexus-prisma/pull/25), [#36](https://github.com/prisma/nexus-prisma/issues/36)) Basic support for Prisma Model field types relating to other Models 1:1
- [x] ([#38](https://github.com/prisma/nexus-prisma/pull/38)) Basic support for Prisma Model field types relating to other Models 1:n

##### Shortterm

- [ ] Support for Prisma Model field types of remaining scalars (`Bytes`, etc.)

##### Midterm

- [x] ([#25](https://github.com/prisma/nexus-prisma/pull/25), [#36](https://github.com/prisma/nexus-prisma/issues/36)) Support for Prisma Model field types relating to other Models 1:1
- [ ] Support for Prisma Model field types relating to other Models 1:n
- [ ] Support for Prisma Model field types relating to other Models n:n

##### Longterm
Expand Down Expand Up @@ -237,6 +242,13 @@ new ApolloServer({

You can project [1:1 relationships](https://www.prisma.io/docs/concepts/components/prisma-schema/relations#one-to-one-relations) into your API.

#### Example: Tests

The integration test suite is a useful reference as it is declarative (easy to read) and gives a known-working example spanning from database all the way to executed GraphQL document.

- [Tests](https://github.com/prisma/nexus-prisma/blob/main/tests/integration/relation1To1.test.ts)
- [Snapshots](https://github.com/prisma/nexus-prisma/blob/main/tests/integration/__snapshots__/relation1To1.test.ts.snap)

#### Example: Full 1:1

```prisma
Expand All @@ -259,6 +271,17 @@ model Profile {

import { User, Profile } from 'nexus-prisma'

queryType({
definition(t) {
t.nonNull.list.nonNull.field('users', {
type: 'User',
resolve(_, __, ctx) {
return ctx.prisma.user.findMany()
},
})
},
})

objectType({
name: User.$name,
definition(t) {
Expand All @@ -273,22 +296,15 @@ objectType({
t.field(Profile.id.name, Profile.id)
},
})

queryType({
definition(t) {
t.list.field('users', {
type: 'User',
resolve(_, __, ctx) {
return ctx.prisma.user.findMany()
},
})
},
})
```

```graphql
# API Schema Represented in GraphQL SDL (this is generated by Nexus)

type Query {
users: [User!]!
}

type User {
id: ID
profile: Profile
Expand Down Expand Up @@ -408,6 +424,129 @@ objectType({
})
```

### Project 1:n Relation

You can project [1:n relationships](https://www.prisma.io/docs/concepts/components/prisma-schema/relations#one-to-many-relations) into your API.

#### Example: Tests

The integration test suite is a useful reference as it is declarative (easy to read) and gives a known-working example spanning from database all the way to executed GraphQL document.

- [Tests](https://github.com/prisma/nexus-prisma/blob/main/tests/integration/relation1ToN.test.ts)
- [Snapshots](https://github.com/prisma/nexus-prisma/blob/main/tests/integration/__snapshots__/relation1ToN.test.ts.snap)

#### Example: Full 1:n

```prisma
// Database Schema
model User {
id String @id
posts Post[]
}
model Post {
id String @id
author User? @relation(fields: [authorId], references: [id])
authorId String
}
```

```ts
// API Schema

import { User, Post } from 'nexus-prisma'

queryType({
definition(t) {
t.nonNull.list.nonNull.field('users', {
type: 'User',
resolve(_, __, ctx) {
return ctx.prisma.user.findMany()
},
})
},
})

objectType({
name: User.$name,
definition(t) {
t.field(User.id.name, User.id)
t.field(User.posts.name, User.posts)
},
})

objectType({
name: Post.$name,
definition(t) {
t.field(Post.id.name, Post.id)
},
})
```

```graphql
# API Schema Represented in GraphQL SDL (this is generated by Nexus)

type Query {
users: [User]
}

type User {
id: ID!
posts: [Post!]!
}

type Post {
id: ID!
}
```

```ts
// Example Database Data (for following example)

await prisma.user.create({
data: {
id: 'user1',
posts: {
create: [{ id: 'post1' }, { id: 'post2' }],
},
},
})
```

```graphql
# Example API Client Query

query {
users {
id
posts {
id
}
}
}
```

```json
{
"data": {
"users": [
{
"id": "user1",
"posts": [
{
"id": "post1"
},
{
"id": "post2"
}
]
}
]
}
}
```

### Prisma ID field to GraphQL ID scalar type mapping

All `@id` fields in your Prisma Schema get projected as `ID` types, not `String` types.
Expand Down Expand Up @@ -521,6 +660,47 @@ PEER_DEPENDENCY_CHECK=false|0

## Recipes

### Project relation with custom resolver logic

Nexus Prisma generates default GraphQL resolvers for your model _relation fields_. However you may want to run custom logic in the resolver. This is easy to do. The following show a few ways.

1. **Wrap Style** You can access the default resolver within your own custom resolver.

```ts
objectType({
name: User.$name,
definition(t) {
t.field(User.id.name, User.id)
t.field(User.posts.name, {
...User.posts,
resolve(...args) {
// Your custom before-logic here
const result = await User.posts.resolve(...args)
// Your custom afer-logic here
return result
},
})
},
})
```

2. **Replace Style** You can simply opt out of using the default resolver completely:

```ts
objectType({
name: User.$name,
definition(t) {
t.field(User.id.name, User.id)
t.field(User.posts.name, {
...User.posts,
resolve(...args) {
// Your custom logic here
},
})
},
})
```

### Supply custom custom scalars to your GraphQL schema

The following is a brief example how you could add your own custom GraphQL custom scalars to satisfy Nexus Prisma. Note that most of the time using the defaults exported by `nexus-prisma/scalars` will probably be good enough for you.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"nexus_prisma": "./dist/cli/nexus-prisma.js"
},
"scripts": {
"reflect:toc": "markdown-toc README.md -i --maxdepth 4",
"reflect:toc": "markdown-toc README.md -i --maxdepth 4 && prettier --write README.md",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint . --ext .ts,.tsx --fix",
Expand Down
40 changes: 38 additions & 2 deletions tests/integration/__snapshots__/relation1To1.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Nullable on Without-Relation-Scalar Side limitation can be worked around by wrapping type in an explicit nonNull: graphqlOperationExecutionResult 1`] = `
Object {
"data": Object {
"users": Array [
Object {
"id": "user1",
"profile": Object {
"id": "profile1",
"user": Object {
"id": "user1",
},
},
},
],
},
}
`;

exports[`Nullable on Without-Relation-Scalar Side limitation can be worked around by wrapping type in an explicit nonNull: graphqlSchemaSDL 1`] = `
"
type Query {
users: [User!]!
}
type User {
id: ID!
profile: Profile!
}
type Profile {
id: ID!
user: User!
}
"
`;

exports[`can project relationship in opposite direction of where @relation is defined, but the field will be nullable: graphqlOperationExecutionResult 1`] = `
Object {
"data": Object {
Expand All @@ -21,7 +57,7 @@ Object {
exports[`can project relationship in opposite direction of where @relation is defined, but the field will be nullable: graphqlSchemaSDL 1`] = `
"
type Query {
users: [User]
users: [User!]!
}
type User {
Expand Down Expand Up @@ -54,7 +90,7 @@ Object {
exports[`can project user-to-profile relationship: graphqlSchemaSDL 1`] = `
"
type Query {
users: [User]
users: [User!]!
}
type User {
Expand Down
38 changes: 38 additions & 0 deletions tests/integration/__snapshots__/relation1ToN.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`can project user-to-posts relationship: graphqlOperationExecutionResult 1`] = `
Object {
"data": Object {
"users": Array [
Object {
"id": "user1",
"posts": Array [
Object {
"id": "post1",
},
Object {
"id": "post2",
},
],
},
],
},
}
`;

exports[`can project user-to-posts relationship: graphqlSchemaSDL 1`] = `
"
type Query {
users: [User!]!
}
type User {
id: ID!
posts: [Post!]!
}
type Post {
id: ID!
}
"
`;

0 comments on commit afa09c6

Please sign in to comment.