Skip to content

Commit bddad62

Browse files
committedApr 13, 2018
feat: new codegen
1 parent a927433 commit bddad62

36 files changed

+7599
-137
lines changed
 

Diff for: ‎README.md

+25-29
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ GraphQL Binding for Prisma services (GraphQL Database)
1010

1111
Here is how it works:
1212

13-
1. Create your Prisma service by defining data model
14-
1. Download generated database schema definition `prisma.graphql` (contains the full CRUD API)
15-
1. Define your application schema, typically called `app.graphql`
16-
1. Instantiate `Prisma` with information about your Prisma service (such as its endpoint and the path to the database schema definition)
17-
1. Implement the resolvers for your application schema by delegating to the underlying Prisma service using the generated delegate resolver functions
13+
1. Create your Prisma service by defining data model
14+
1. Download generated database schema definition `prisma.graphql` (contains the full CRUD API)
15+
1. Define your application schema, typically called `app.graphql`
16+
1. Instantiate `Prisma` with information about your Prisma service (such as its endpoint and the path to the database schema definition)
17+
1. Implement the resolvers for your application schema by delegating to the underlying Prisma service using the generated delegate resolver functions
1818

1919
> **Note**: If you're using a [GraphQL boilerplate](https://github.com/graphql-boilerplates/) project (e.g. with `graphql create`), the Prisma binding will already be configured and a few example resolvers implemented for you. You can either try the _dynamic binding_ (e.g. in the [`node-basic`](https://github.com/graphql-boilerplates/node-graphql-server/tree/master/basic) boilerplate) or a _static binding_ (e.g in the [`typescript-basic`](https://github.com/graphql-boilerplates/typescript-graphql-server/tree/master/basic) boilerplate).
2020
@@ -73,8 +73,8 @@ The API also allows to ask whether a specific node exists in your Prisma databas
7373
prisma.exists.Post({
7474
id: 'abc',
7575
author: {
76-
name: 'Sarah'
77-
}
76+
name: 'Sarah',
77+
},
7878
})
7979
```
8080

@@ -84,13 +84,13 @@ prisma.exists.Post({
8484

8585
The `PrismaOptions` type has the following fields:
8686

87-
| Key | Required | Type | Default | Note |
88-
| --- | --- | --- | --- | --- |
89-
| `typeDefs` | Yes | `string` | - | Type definition string or file path to the schema definition of your Prisma service (typically a file called `database.graphql`) |
90-
| `endpoint` | Yes | `string` | - | The endpoint of your Prisma service |
91-
| `secret` | Yes | `string` | - | The secret of your Prisma service |
92-
| `fragmentReplacements` | No | `FragmentReplacements` | `null` | A list of GraphQL fragment definitions, specifying fields that are required for the resolver to function correctly |
93-
| `debug` | No | `boolean` | `false` | Log all queries/mutations to the console |
87+
| Key | Required | Type | Default | Note |
88+
| ---------------------- | -------- | ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
89+
| `typeDefs` | Yes | `string` | - | Type definition string or file path to the schema definition of your Prisma service (typically a file called `database.graphql`) |
90+
| `endpoint` | Yes | `string` | - | The endpoint of your Prisma service |
91+
| `secret` | Yes | `string` | - | The secret of your Prisma service |
92+
| `fragmentReplacements` | No | `FragmentReplacements` | `null` | A list of GraphQL fragment definitions, specifying fields that are required for the resolver to function correctly |
93+
| `debug` | No | `boolean` | `false` | Log all queries/mutations to the console |
9494

9595
### `query` and `mutation`
9696

@@ -106,10 +106,10 @@ Delegate resolver have the following interface:
106106

107107
The input arguments are used as follows:
108108

109-
- `args`: An object carrying potential arguments for the query/mutation
110-
- `info`: An object representing the selection set of the query/mutation, either expressed directly as a string or in the form of `GraphQLResolveInfo` (you can find more info about the `GraphQLResolveInfo` type [here](http://graphql.org/graphql-js/type/#graphqlobjecttype))
109+
* `args`: An object carrying potential arguments for the query/mutation
110+
* `info`: An object representing the selection set of the query/mutation, either expressed directly as a string or in the form of `GraphQLResolveInfo` (you can find more info about the `GraphQLResolveInfo` type [here](http://graphql.org/graphql-js/type/#graphqlobjecttype))
111111

112-
The generic type `T` corresponds to the type of the respective field.
112+
The generic type `T` corresponds to the type of the respective field.
113113

114114
### `exists`
115115

@@ -135,8 +135,7 @@ const query = `
135135

136136
const variables = { userId: 'abc' }
137137

138-
prisma.request(query, variables)
139-
.then(result => console.log(result))
138+
prisma.request(query, variables).then(result => console.log(result))
140139
// sample result:
141140
// {"data": { "user": { "id": "abc", "name": "Sarah" } } }
142141
```
@@ -146,12 +145,12 @@ prisma.request(query, variables)
146145
If you just want to forward a query to the exact same underlying prisma query, you can use `forwardTo`:
147146

148147
```js
149-
const {forwardTo} = require('prisma-binding')
148+
const { forwardTo } = require('prisma-binding')
150149

151150
const resolvers = {
152151
Query: {
153-
posts: forwardTo('db')
154-
}
152+
posts: forwardTo('db'),
153+
},
155154
}
156155

157156
const server = new GraphQLServer({
@@ -168,17 +167,14 @@ const server = new GraphQLServer({
168167
}),
169168
})
170169

171-
server.start(
172-
() => console.log(`Server is running on http://localhost:4000`),
173-
)
170+
server.start(() => console.log(`Server is running on http://localhost:4000`))
174171
```
175172

176-
177173
## Usage
178174

179-
- [graphql-boilerplate](https://github.com/graphcool/graphql-boilerplate).
180-
- [graphql-server-example](https://github.com/graphcool/graphql-server-example).
175+
* [graphql-boilerplate](https://github.com/graphcool/graphql-boilerplate).
176+
* [graphql-server-example](https://github.com/graphcool/graphql-server-example).
181177

182178
## Next steps
183179

184-
- Code generation at build-time for the auto-generated delegate resolvers
180+
* Code generation at build-time for the auto-generated delegate resolvers

Diff for: ‎packages/graphql-codegen-prisma-binding/README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# graphql-codegen-binding
2+
3+
## Usage
4+
5+
### CLI
6+
7+
```sh
8+
$ npm install -g graphql-codegen-binding
9+
$ graphql-codegen-binding
10+
11+
Usage: graphql-codegen-binding -s [schema] -e [endpoint] -h [headers] -g [generator] -t [target]
12+
13+
Options:
14+
--help Show help [boolean]
15+
--version Show version number [boolean]
16+
--schema, -s Path to schema.graphql file [string]
17+
--endpoint, -e GraphQL endpoint to fetch schema from [string]
18+
--headers, -h Header to use for downloading the schema (with endpoint URL)
19+
[string]
20+
--generator, -g Type of the generator. Available generators: typescript,
21+
javascript [string] [required]
22+
--target, -t Target file. Example: schema.ts [string] [required]
23+
```
24+
25+
### Typescript
26+
27+
```ts
28+
import { generateCode } from 'graphql-codegen-binding'
29+
import * as fs from 'fs'
30+
31+
const code = generateCode(fs.readFileSync('schema.graphql'), 'typescript')
32+
33+
fs.writeFileSync('MyBinding.ts', code)
34+
```

Diff for: ‎packages/graphql-codegen-prisma-binding/package.json

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"name": "graphql-codegen-prisma-binding",
3+
"version": "0.0.1",
4+
"main": "dist/index.js",
5+
"types": "dist/index.d.ts",
6+
"repository": {
7+
"url": "https://github.com/graphcool/graphql-codegen-binding.git"
8+
},
9+
"bin": {
10+
"graphql-codegen-prisma-binding": "./dist/bin.js"
11+
},
12+
"files": [
13+
"dist"
14+
],
15+
"contributors": [
16+
{
17+
"name": "Tim Suchanek",
18+
"email": "suchanek@prisma.io"
19+
}
20+
],
21+
"license": "MIT",
22+
"devDependencies": {
23+
"@types/graphql": "^0.13.0",
24+
"@types/jest": "^22.2.2",
25+
"@types/node": "^9.6.2",
26+
"jest": "^22.4.3",
27+
"prettier": "^1.10.2",
28+
"ts-jest": "^22.4.2",
29+
"tslint": "^5.6.0",
30+
"typescript": "^2.6.2"
31+
},
32+
"scripts": {
33+
"test": "jest",
34+
"build": "tsc -d && chmod +x dist/bin.js",
35+
"lint": "tslint src/**/*.ts",
36+
"precommit": "lint-staged",
37+
"prepublishOnly": "yarn lint && yarn test && yarn build"
38+
},
39+
"peerDependencies": {
40+
"graphql": "^0.11.0 || ^0.12.0 || ^0.13.0"
41+
},
42+
"jest": {
43+
"moduleFileExtensions": [
44+
"ts",
45+
"tsx",
46+
"js",
47+
"jsx",
48+
"json"
49+
],
50+
"rootDir": "./src",
51+
"transform": {
52+
"^.+\\.(ts|tsx)$": "../node_modules/ts-jest/preprocessor.js"
53+
},
54+
"testMatch": [
55+
"**/*.test.(ts|js)"
56+
],
57+
"globals": {
58+
"ts-jest": {
59+
"tsConfigFile": "../tsconfig.json"
60+
}
61+
}
62+
},
63+
"dependencies": {
64+
"graphql": "^0.11.0 || ^0.12.0 || ^0.13.0",
65+
"graphql-codegen-binding": "^0.0.15",
66+
"graphql-config": "2.0.1",
67+
"mkdirp": "^0.5.1",
68+
"yargs": "^11.0.0"
69+
}
70+
}

Diff for: ‎packages/graphql-codegen-prisma-binding/src/bin.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env node
2+
3+
import * as yargs from 'yargs'
4+
import { generateCode } from '.'
5+
6+
const argv = yargs
7+
.usage(
8+
`Usage: $0 -s [schema] -e [endpoint] -h [headers] -g [generator] -t [target]`,
9+
)
10+
.options({
11+
schema: {
12+
alias: 's',
13+
describe: 'Path to schema.graphql file',
14+
type: 'string',
15+
},
16+
endpoint: {
17+
alias: 'e',
18+
describe: 'GraphQL endpoint to fetch schema from',
19+
type: 'string',
20+
},
21+
headers: {
22+
alias: 'h',
23+
describe: 'Header to use for downloading the schema (with endpoint URL)',
24+
type: 'string',
25+
},
26+
generator: {
27+
alias: 'g',
28+
describe:
29+
'Type of the generator. Available generators: typescript, javascript',
30+
type: 'string',
31+
},
32+
target: {
33+
alias: 't',
34+
describe: 'Target file. Example: schema.ts',
35+
type: 'string',
36+
},
37+
})
38+
.demandOption(['g', 't']).argv
39+
40+
run(argv)
41+
42+
async function run(argv) {
43+
const endpointHeaders = {}
44+
const headers = argv.headers
45+
? Array.isArray(argv.headers) ? argv.headers : [argv.headers]
46+
: undefined
47+
if (headers) {
48+
Object.assign(
49+
endpointHeaders,
50+
...headers.map(h => ({ [h.split('=')[0]]: h.split('=')[1] })),
51+
)
52+
}
53+
54+
const { generator, target, endpoint } = argv
55+
await generateCode({
56+
schemaPath: argv.schema,
57+
generator,
58+
target,
59+
endpoint,
60+
headers: endpointHeaders,
61+
})
62+
63+
console.log('Done generating binding')
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as fs from 'fs'
2+
import { GraphQLEndpoint } from 'graphql-config'
3+
import { makeBinding } from './makeBinding'
4+
import { GeneratorType } from './types'
5+
import * as mkdirp from 'mkdirp'
6+
import * as path from 'path'
7+
8+
export interface CodeGenerationInput {
9+
schemaPath?: string
10+
schema?: string
11+
endpoint?: string
12+
generator: GeneratorType
13+
target: string
14+
headers?: any
15+
}
16+
17+
export async function generateCode(argv: CodeGenerationInput) {
18+
if (!argv.schema && !argv.schemaPath && !argv.endpoint) {
19+
throw new Error(
20+
'Please either provide the schema or the endpoint you want to get the schema from.',
21+
)
22+
}
23+
24+
const schema = argv.schema
25+
? argv.schema
26+
: argv.schemaPath
27+
? fs.readFileSync(argv.schemaPath, 'utf-8')
28+
: await downloadFromEndpointUrl(argv)
29+
30+
const code = makeBinding(schema, argv.generator)
31+
mkdirp(path.dirname(argv.target))
32+
fs.writeFileSync(argv.target, code)
33+
}
34+
35+
function downloadFromEndpointUrl(argv) {
36+
const endpoint = new GraphQLEndpoint({
37+
url: argv.endpoint,
38+
headers: argv.headers,
39+
})
40+
41+
return endpoint.resolveSchemaSDL()
42+
}

Diff for: ‎packages/graphql-codegen-prisma-binding/src/generators/__snapshots__/javascript.test.ts.snap

+1,008
Large diffs are not rendered by default.

Diff for: ‎packages/graphql-codegen-prisma-binding/src/generators/__snapshots__/typescript.test.ts.snap

+1,427
Large diffs are not rendered by default.

Diff for: ‎packages/graphql-codegen-prisma-binding/src/generators/fixtures/schema.graphql

+913
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { generator as typescriptGenerator } from './typescript'
2+
import { generator as javascriptGenerator } from './javascript'
3+
import { Generator } from 'graphql-codegen-binding'
4+
5+
export const generators: { [key: string]: Generator } = {
6+
typescript: typescriptGenerator,
7+
javascript: javascriptGenerator,
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { makeBinding } from '../makeBinding'
2+
import * as fs from 'fs'
3+
4+
const schema = fs.readFileSync(__dirname + '/fixtures/schema.graphql', 'utf-8')
5+
test('typescript generator', () => {
6+
expect(makeBinding(schema, 'javascript')).toMatchSnapshot()
7+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { GraphQLObjectType, GraphQLFieldMap } from 'graphql'
2+
3+
import { javascriptGenerator, Generator } from 'graphql-codegen-binding'
4+
import { isWrappingType } from 'graphql'
5+
import { isListType } from 'graphql'
6+
import { GraphQLInputObjectType } from 'graphql'
7+
8+
export const generator: Generator = {
9+
...javascriptGenerator,
10+
Main: renderMainMethod,
11+
Header: renderHeader,
12+
}
13+
14+
const scalarMapping = {
15+
Int: 'number',
16+
String: 'string',
17+
ID: 'string | number',
18+
Float: 'number',
19+
Boolean: 'boolean',
20+
}
21+
22+
export function renderExistsFields(fields: GraphQLFieldMap<any, any>): string {
23+
return Object.keys(fields)
24+
.map(f => {
25+
const field = fields[f]
26+
let type = field.type
27+
let foundList = false
28+
// Traverse the wrapping types (if any)
29+
while (isWrappingType(type)) {
30+
type = type.ofType
31+
// One of those wrappings need to be a GraphQLList for this field to qualify
32+
foundList = foundList || isListType(type)
33+
}
34+
if (foundList) {
35+
const whereType = (field.args.find(a => a.name === 'where')!
36+
.type as GraphQLInputObjectType).name
37+
return ` ${
38+
type.name
39+
}: (where: ${whereType}): Promise<boolean> => super.existsDelegate('query', '${
40+
field.name
41+
}', { where }, {}, '{ id }')`
42+
}
43+
})
44+
.filter(f => f)
45+
.join(',\n')
46+
}
47+
48+
function renderHeader(schema: string): string {
49+
return `const { Prisma } = require('prisma-binding')
50+
const { GraphQLResolveInfo } = require('graphql')
51+
const typeDefs = \`
52+
${schema}\``
53+
}
54+
55+
function renderMainMethod(
56+
queryType: GraphQLObjectType,
57+
mutationType?: GraphQLObjectType | null,
58+
subscriptionType?: GraphQLObjectType | null,
59+
) {
60+
return `module.exports.Prisma = class Binding extends Prisma {
61+
62+
constructor({ endpoint, secret, fragmentReplacements, debug }) {
63+
super({ typeDefs, endpoint, secret, fragmentReplacements, debug });
64+
var self = this
65+
this.exists = {
66+
${renderExistsFields(queryType.getFields())}
67+
}
68+
this.query = {
69+
${javascriptGenerator.MainFields!('query', queryType.getFields())}
70+
}${
71+
mutationType
72+
? `
73+
74+
this.mutation = {
75+
${javascriptGenerator.MainFields!('mutation', mutationType.getFields())}
76+
}`
77+
: ''
78+
}${
79+
subscriptionType
80+
? `
81+
82+
this.subscription = {
83+
${javascriptGenerator.MainSubscriptionFields!(subscriptionType.getFields())}
84+
}`
85+
: ''
86+
}
87+
}
88+
89+
delegate(operation, field, args, context, info) {
90+
return super.delegate(operation, field, args, context, info)
91+
}
92+
}`
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { makeBinding } from '../makeBinding'
2+
import * as fs from 'fs'
3+
4+
const schema = fs.readFileSync(__dirname + '/fixtures/schema.graphql', 'utf-8')
5+
test('typescript generator', () => {
6+
expect(makeBinding(schema, 'typescript')).toMatchSnapshot()
7+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { GraphQLObjectType, GraphQLFieldMap } from 'graphql'
2+
3+
import { typescriptGenerator, Generator } from 'graphql-codegen-binding'
4+
import { isWrappingType } from 'graphql'
5+
import { isListType } from 'graphql'
6+
import { GraphQLInputObjectType } from 'graphql'
7+
8+
export const generator: Generator = {
9+
...typescriptGenerator,
10+
Main: renderMainMethod,
11+
Header: renderHeader,
12+
}
13+
14+
const scalarMapping = {
15+
Int: 'number',
16+
String: 'string',
17+
ID: 'string | number',
18+
Float: 'number',
19+
Boolean: 'boolean',
20+
}
21+
22+
export function renderExistsFields(fields: GraphQLFieldMap<any, any>): string {
23+
return Object.keys(fields)
24+
.map(f => {
25+
const field = fields[f]
26+
let type = field.type
27+
let foundList = false
28+
// Traverse the wrapping types (if any)
29+
while (isWrappingType(type)) {
30+
type = type.ofType
31+
// One of those wrappings need to be a GraphQLList for this field to qualify
32+
foundList = foundList || isListType(type)
33+
}
34+
if (foundList) {
35+
const whereType = (field.args.find(a => a.name === 'where')!
36+
.type as GraphQLInputObjectType).name
37+
return ` ${
38+
type.name
39+
}: (where: ${whereType}): Promise<boolean> => super.existsDelegate('query', '${
40+
field.name
41+
}', { where }, {}, '{ id }')`
42+
}
43+
})
44+
.filter(f => f)
45+
.join(',\n')
46+
}
47+
48+
function renderHeader(schema: string): string {
49+
return `import { Prisma as BasePrisma, BasePrismaOptions } from 'prisma-binding'
50+
import { GraphQLResolveInfo } from 'graphql'
51+
52+
export const typeDefs = \`
53+
${schema}\``
54+
}
55+
56+
function renderMainMethod(
57+
queryType: GraphQLObjectType,
58+
mutationType?: GraphQLObjectType | null,
59+
subscriptionType?: GraphQLObjectType | null,
60+
) {
61+
return `export class Prisma extends BasePrisma {
62+
63+
constructor({ endpoint, secret, fragmentReplacements, debug }: BasePrismaOptions) {
64+
super({ typeDefs, endpoint, secret, fragmentReplacements, debug });
65+
}
66+
67+
exists = {
68+
${renderExistsFields(queryType.getFields())}
69+
}
70+
71+
query: Query = {
72+
${typescriptGenerator.MainFields!('query', queryType.getFields())}
73+
}${
74+
mutationType
75+
? `
76+
77+
mutation: Mutation = {
78+
${typescriptGenerator.MainFields!('mutation', mutationType.getFields())}
79+
}`
80+
: ''
81+
}${
82+
subscriptionType
83+
? `
84+
85+
subscription: Subscription = {
86+
${typescriptGenerator.MainSubscriptionFields!(subscriptionType.getFields())}
87+
}`
88+
: ''
89+
}
90+
}`
91+
}

Diff for: ‎packages/graphql-codegen-prisma-binding/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { generateCode } from './generateCode'
2+
export { makeBinding } from './makeBinding'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
DocumentNode,
3+
Kind,
4+
ObjectTypeDefinitionNode,
5+
OperationTypeDefinitionNode,
6+
parse,
7+
} from 'graphql'
8+
import { GraphQLSchema } from 'graphql/type/schema'
9+
import { buildASTSchema } from 'graphql/utilities/buildASTSchema'
10+
import { generators } from './generators'
11+
import { GeneratorType } from './types'
12+
import { makeBinding as makeGraphQLBinding } from 'graphql-codegen-binding'
13+
14+
/**
15+
* The schema contains incompatible characters sometimes, e.g.
16+
* data types in comments are emphasized with "`", which represents
17+
* template strings in ES2015 and TypeScript. This function
18+
* replaces those characters with sane defaults.
19+
*
20+
* @param schema {String} The serialized schema
21+
* @returns {String}
22+
*
23+
*/
24+
const sanitizeSchema = (schema: string) => schema.replace(/\`/g, "'")
25+
26+
export function makeBinding(
27+
schema: string,
28+
generatorName: GeneratorType,
29+
): string {
30+
return makeGraphQLBinding(schema, generators[generatorName])
31+
}

Diff for: ‎packages/graphql-codegen-prisma-binding/src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type GeneratorType = 'typescript' | 'javascript'
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"forceConsistentCasingInFileNames": true,
4+
"strictNullChecks": true,
5+
"sourceMap": true,
6+
"outDir": "dist",
7+
"moduleResolution": "node",
8+
"lib": ["es2017", "dom", "esnext.asynciterable"],
9+
"target": "es5",
10+
"rootDir": "src",
11+
"skipLibCheck": true
12+
},
13+
"exclude": ["node_modules", "dist"]
14+
}

Diff for: ‎packages/graphql-codegen-prisma-binding/yarn.lock

+3,534
Large diffs are not rendered by default.

Diff for: ‎packages/prisma-binding/.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
.idea
3+
dist
4+
.DS_Store
5+
*.log

Diff for: ‎packages/prisma-binding/README.md

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# prisma-binding
2+
3+
[![CircleCI](https://circleci.com/gh/graphcool/prisma-binding.svg?style=shield)](https://circleci.com/gh/graphcool/prisma-binding) [![npm version](https://badge.fury.io/js/prisma-binding.svg)](https://badge.fury.io/js/prisma-binding)
4+
5+
GraphQL Binding for Prisma services (GraphQL Database)
6+
7+
## Overview
8+
9+
`prisma-binding` provides a convenience layer for building GraphQL servers on top of Prisma services. In short, it simplifies implementing your GraphQL resolvers by _delegating_ execution of queries (or mutations) to the API of the underlying Prisma database service.
10+
11+
Here is how it works:
12+
13+
1. Create your Prisma service by defining data model
14+
1. Download generated database schema definition `prisma.graphql` (contains the full CRUD API)
15+
1. Define your application schema, typically called `app.graphql`
16+
1. Instantiate `Prisma` with information about your Prisma service (such as its endpoint and the path to the database schema definition)
17+
1. Implement the resolvers for your application schema by delegating to the underlying Prisma service using the generated delegate resolver functions
18+
19+
> **Note**: If you're using a [GraphQL boilerplate](https://github.com/graphql-boilerplates/) project (e.g. with `graphql create`), the Prisma binding will already be configured and a few example resolvers implemented for you. You can either try the _dynamic binding_ (e.g. in the [`node-basic`](https://github.com/graphql-boilerplates/node-graphql-server/tree/master/basic) boilerplate) or a _static binding_ (e.g in the [`typescript-basic`](https://github.com/graphql-boilerplates/typescript-graphql-server/tree/master/basic) boilerplate).
20+
21+
## Install
22+
23+
```sh
24+
yarn add prisma-binding
25+
# or
26+
npm install --save prisma-binding
27+
```
28+
29+
## Example
30+
31+
Consider the following data model for your Prisma service:
32+
33+
```graphql
34+
type User {
35+
id: ID! @unique
36+
name: String
37+
}
38+
```
39+
40+
If you instantiate `Prisma` based on this service, you'll be able to send the following queries/mutations:
41+
42+
```js
43+
// Instantiate `Prisma` based on concrete service
44+
const prisma = new Prisma({
45+
typeDefs: 'schemas/database.graphql',
46+
endpoint: 'https://us1.prisma.sh/demo/my-service/dev'
47+
secret: 'my-super-secret-secret'
48+
})
49+
50+
// Retrieve `name` of a specific user
51+
prisma.query.user({ where { id: 'abc' } }, '{ name }')
52+
53+
// Retrieve `id` and `name` of all users
54+
prisma.query.users(null, '{ id name }')
55+
56+
// Create new user called `Sarah` and retrieve the `id`
57+
prisma.mutation.createUser({ data: { name: 'Sarah' } }, '{ id }')
58+
59+
// Update name of a specific user and retrieve the `id`
60+
prisma.mutation.updateUser({ where: { id: 'abc' }, data: { name: 'Sarah' } }, '{ id }')
61+
62+
// Delete a specific user and retrieve the `name`
63+
prisma.mutation.deleteUser({ where: { id: 'abc' } }, '{ id }')
64+
```
65+
66+
Under the hood, each of these function calls is simply translated into an actual HTTP request against your Prisma service (using [`graphql-request`](https://github.com/graphcool/graphql-request)).
67+
68+
The API also allows to ask whether a specific node exists in your Prisma database:
69+
70+
```js
71+
// Ask whether a post exists with `id` equal to `abc` and whose
72+
// `author` is called `Sarah` (return boolean value)
73+
prisma.exists.Post({
74+
id: 'abc',
75+
author: {
76+
name: 'Sarah'
77+
}
78+
})
79+
```
80+
81+
## API
82+
83+
### `constructor(options: PrismaOptions): Prisma`
84+
85+
The `PrismaOptions` type has the following fields:
86+
87+
| Key | Required | Type | Default | Note |
88+
| --- | --- | --- | --- | --- |
89+
| `typeDefs` | Yes | `string` | - | Type definition string or file path to the schema definition of your Prisma service (typically a file called `database.graphql`) |
90+
| `endpoint` | Yes | `string` | - | The endpoint of your Prisma service |
91+
| `secret` | Yes | `string` | - | The secret of your Prisma service |
92+
| `fragmentReplacements` | No | `FragmentReplacements` | `null` | A list of GraphQL fragment definitions, specifying fields that are required for the resolver to function correctly |
93+
| `debug` | No | `boolean` | `false` | Log all queries/mutations to the console |
94+
95+
### `query` and `mutation`
96+
97+
`query` and `mutation` are public properties on your `Prisma` instance. They both are of type `Query` and expose a number of auto-generated delegate resolver functions that are named after the fields on the `Query` and `Mutation` types in your Prisma database schema.
98+
99+
Each of these delegate resolvers in essence provides a convenience API for sending queries/mutations to your Prisma service, so you don't have to spell out the full query/mutation from scratch and worry about sending it over HTTP. This is all handled by the delegate resolver function under the hood.
100+
101+
Delegate resolver have the following interface:
102+
103+
```js
104+
(args: any, info: GraphQLResolveInfo | string): Promise<T>
105+
```
106+
107+
The input arguments are used as follows:
108+
109+
- `args`: An object carrying potential arguments for the query/mutation
110+
- `info`: An object representing the selection set of the query/mutation, either expressed directly as a string or in the form of `GraphQLResolveInfo` (you can find more info about the `GraphQLResolveInfo` type [here](http://graphql.org/graphql-js/type/#graphqlobjecttype))
111+
112+
The generic type `T` corresponds to the type of the respective field.
113+
114+
### `exists`
115+
116+
`exists` also is a public property on your `Prisma` instance. Similar to `query` and `mutation`, it also exposes a number of auto-generated functions. However, it exposes only a single function per type. This function is named according to the root field that allows the retrieval of a single node of that type (e.g. `User` for a type called `User`). It takes a `where` object as an input argument and returns a `boolean` value indicating whether the condition expressed with `where` is met.
117+
118+
This function enables you to easily check whether a node of a specific type exists in your Prisma database.
119+
120+
### `request`
121+
122+
The `request` method lets you send GraphQL queries/mutations to your Prisma service. The functionality is identical to the auto-generated delegate resolves, but the API is more verbose as you need to spell out the full query/mutation. `request` uses [`graphql-request`](https://github.com/graphcool/graphql-request) under the hood.
123+
124+
Here is an example of how it can be used:
125+
126+
```js
127+
const query = `
128+
query ($userId: ID!){
129+
user(id: $userId) {
130+
id
131+
name
132+
}
133+
}
134+
`
135+
136+
const variables = { userId: 'abc' }
137+
138+
prisma.request(query, variables)
139+
.then(result => console.log(result))
140+
// sample result:
141+
// {"data": { "user": { "id": "abc", "name": "Sarah" } } }
142+
```
143+
144+
### `forwardTo`
145+
146+
If you just want to forward a query to the exact same underlying prisma query, you can use `forwardTo`:
147+
148+
```js
149+
const {forwardTo} = require('prisma-binding')
150+
151+
const resolvers = {
152+
Query: {
153+
posts: forwardTo('db')
154+
}
155+
}
156+
157+
const server = new GraphQLServer({
158+
typeDefs: './src/schema.graphql',
159+
resolvers,
160+
context: req => ({
161+
...req,
162+
db: new Prisma({
163+
typeDefs: 'src/generated/prisma.graphql',
164+
endpoint: '...',
165+
secret: 'mysecret123',
166+
}),
167+
debug: true,
168+
}),
169+
})
170+
171+
server.start(
172+
() => console.log(`Server is running on http://localhost:4000`),
173+
)
174+
```
175+
176+
177+
## Usage
178+
179+
- [graphql-boilerplate](https://github.com/graphcool/graphql-boilerplate).
180+
- [graphql-server-example](https://github.com/graphcool/graphql-server-example).
181+
182+
## Next steps
183+
184+
- Code generation at build-time for the auto-generated delegate resolvers

Diff for: ‎package.json renamed to ‎packages/prisma-binding/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "prisma-binding",
3-
"version": "0.0.0-semantic-release",
3+
"version": "1.6.0-beta.1",
44
"main": "dist/src/index.js",
55
"typings": "./dist/src/index.d.ts",
66
"files": [
@@ -30,7 +30,7 @@
3030
"apollo-link-error": "1.0.7",
3131
"apollo-link-ws": "1.0.7",
3232
"cross-fetch": "2.0.0",
33-
"graphql-binding": "1.2.5",
33+
"graphql-binding": "^1.4.0-beta.1",
3434
"graphql-import": "0.4.5",
3535
"graphql-tools": "2.21.0",
3636
"http-link-dataloader": "^0.1.2",

Diff for: ‎packages/prisma-binding/renovate.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["config:base", "docker:disable"]
3+
}

Diff for: ‎src/Prisma.ts renamed to ‎packages/prisma-binding/src/Prisma.ts

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Binding } from 'graphql-binding'
1+
import { Binding, Handler, SubscriptionHandler } from 'graphql-binding'
22
import { Exists, PrismaOptions, QueryMap, SubscriptionMap } from './types'
33
import { sign } from 'jsonwebtoken'
44
import { makePrismaLink } from './link'
@@ -8,7 +8,6 @@ import { importSchema } from 'graphql-import'
88
import { GraphQLNamedType, GraphQLSchema } from 'graphql'
99
import { SharedLink } from './SharedLink'
1010
import { makeRemoteExecutableSchema } from 'graphql-tools'
11-
import { Handler, SubscriptionHandler } from './Handler'
1211

1312
const typeDefsCache: { [schemaPath: string]: string } = {}
1413
const remoteSchemaCache: { [typeDefs: string]: GraphQLSchema } = {}
@@ -62,11 +61,17 @@ export class Prisma extends Binding<QueryMap, SubscriptionMap> {
6261
sharedLink.setInnerLink(link)
6362
}
6463

65-
super({ schema: remoteSchema, fragmentReplacements, before, handler: Handler, subscriptionHandler: SubscriptionHandler })
64+
super({
65+
schema: remoteSchema,
66+
fragmentReplacements,
67+
before,
68+
handler: Handler,
69+
subscriptionHandler: SubscriptionHandler,
70+
})
6671

6772
this.exists = new Proxy(
6873
{},
69-
new ExistsHandler(remoteSchema, this.existsDelegate.bind(this))
74+
new ExistsHandler(remoteSchema, this.existsDelegate.bind(this)),
7075
)
7176
}
7277

@@ -82,18 +87,21 @@ export class Prisma extends Binding<QueryMap, SubscriptionMap> {
8287
info?: GraphQLResolveInfo | string,
8388
): Promise<boolean> {
8489
this.before()
85-
return this
86-
.delegate(operation, fieldName, args, context, info)
87-
.then(res => res.length > 0)
90+
return this.delegate(operation, fieldName, args, info, context).then(
91+
res => res.length > 0,
92+
)
8893
}
8994
}
9095

9196
class ExistsHandler implements ProxyHandler<Prisma> {
92-
constructor(private schema: GraphQLSchema, private delegate: any) { }
97+
constructor(private schema: GraphQLSchema, private delegate: any) {}
9398

9499
get(target: any, typeName: string) {
95100
return async (where: { [key: string]: any }): Promise<boolean> => {
96-
const rootFieldName: string = this.findRootFieldName(typeName, this.schema)
101+
const rootFieldName: string = this.findRootFieldName(
102+
typeName,
103+
this.schema,
104+
)
97105
const args = { where }
98106
const info = buildExistsInfo(rootFieldName, this.schema)
99107
return this.delegate('query', rootFieldName, args, {}, info)
@@ -135,8 +143,8 @@ function getCachedTypeDefs(schemaPath: string): string {
135143
}
136144

137145
function getCachedRemoteSchema(
138-
typeDefs: string,
139-
link: SharedLink
146+
typeDefs: string,
147+
link: SharedLink,
140148
): GraphQLSchema {
141149
if (remoteSchemaCache[typeDefs]) {
142150
return remoteSchemaCache[typeDefs]
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Diff for: ‎yarn.lock renamed to ‎packages/prisma-binding/yarn.lock

+15-3
Original file line numberDiff line numberDiff line change
@@ -1379,19 +1379,27 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2:
13791379
version "4.1.11"
13801380
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
13811381

1382-
graphql-binding@1.2.5:
1383-
version "1.2.5"
1384-
resolved "https://registry.yarnpkg.com/graphql-binding/-/graphql-binding-1.2.5.tgz#1c45b9da055cb0722f0348b6354be2e11b0a8c17"
1382+
graphql-binding@^1.4.0-beta.1:
1383+
version "1.4.0-beta.1"
1384+
resolved "https://registry.yarnpkg.com/graphql-binding/-/graphql-binding-1.4.0-beta.1.tgz#7c7ba01e7fc08fd4e56089264a4dda89c2c94a71"
13851385
dependencies:
1386+
graphql-import "^0.5.0"
13861387
graphql-tools "2.21.0"
13871388
iterall "1.2.2"
1389+
object-path-immutable "^1.0.1"
13881390

13891391
graphql-import@0.4.5:
13901392
version "0.4.5"
13911393
resolved "https://registry.yarnpkg.com/graphql-import/-/graphql-import-0.4.5.tgz#e2f18c28d335733f46df8e0733d8deb1c6e2a645"
13921394
dependencies:
13931395
lodash "^4.17.4"
13941396

1397+
graphql-import@^0.5.0:
1398+
version "0.5.0"
1399+
resolved "https://registry.yarnpkg.com/graphql-import/-/graphql-import-0.5.0.tgz#5f678a6d4636d02a829308884aa1f2fa2197f06d"
1400+
dependencies:
1401+
lodash "^4.17.4"
1402+
13951403
graphql-tools@2.21.0:
13961404
version "2.21.0"
13971405
resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-2.21.0.tgz#c0d0fbda6f40a87c8d267a2989ade2ae8b9a288e"
@@ -2196,6 +2204,10 @@ object-assign@^4.0.1, object-assign@^4.1.0:
21962204
version "4.1.1"
21972205
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
21982206

2207+
object-path-immutable@^1.0.1:
2208+
version "1.0.1"
2209+
resolved "https://registry.yarnpkg.com/object-path-immutable/-/object-path-immutable-1.0.1.tgz#3c424052be3c54ec82def03f1b11b0c6e085c7ff"
2210+
21992211
object.omit@^2.0.0:
22002212
version "2.0.1"
22012213
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"

Diff for: ‎renovate.json

-5
This file was deleted.

Diff for: ‎src/Handler.ts

-87
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.