Compare commits
103 Commits
1.1.0-next
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
9e5c27c509 | |
|
|
601c2f03f4 | |
|
|
4e085dc7b2 | |
|
|
1e6b9212d3 | |
|
|
4d8e37177b | |
|
|
eec4b91091 | |
|
|
8e65081539 | |
|
|
7ee48c4f2c | |
|
|
e9dc7e0c4a | |
|
|
9875e90dae | |
|
|
9a1050792b | |
|
|
df9cb700a9 | |
|
|
123dc61fac | |
|
|
251af9461b | |
|
|
b906288c58 | |
|
|
11d028277e | |
|
|
509c246e88 | |
|
|
aaa45204b6 | |
|
|
7349e3633c | |
|
|
892af670a4 | |
|
|
bc12ca0f8e | |
|
|
0f37c3e9d4 | |
|
|
6c1530a3d5 | |
|
|
92f20dc9e9 | |
|
|
bbc969a5b0 | |
|
|
0d06f26b12 | |
|
|
e901bebd51 | |
|
|
87a82a28b1 | |
|
|
3de7f399c6 | |
|
|
45b9d5a926 | |
|
|
b92545fe4f | |
|
|
4d035019a4 | |
|
|
9ec3442539 | |
|
|
c0e55b53b4 | |
|
|
a7c26ba587 | |
|
|
703d5595da | |
|
|
02015ede84 | |
|
|
2934d6d38c | |
|
|
5e3b837ec0 | |
|
|
b24d132d38 | |
|
|
e5658cadba | |
|
|
333dfb8757 | |
|
|
02a73b5d74 | |
|
|
7e744aa0d3 | |
|
|
e65b5a7952 | |
|
|
d4c6260774 | |
|
|
6de1cb6917 | |
|
|
555ae79ded | |
|
|
a003ff074f | |
|
|
855a482c54 | |
|
|
266f1a82ce | |
|
|
380ed9d4ca | |
|
|
5984ee122e | |
|
|
a914cc33d1 | |
|
|
03b5ffd24a | |
|
|
4531519e6c | |
|
|
8e0ce1b4a1 | |
|
|
df7d1370f9 | |
|
|
ebcdb29a3e | |
|
|
81ec5c0f08 | |
|
|
2b0708d675 | |
|
|
2759c551c7 | |
|
|
10ea65f6f1 | |
|
|
690abb7299 | |
|
|
2e1c32b794 | |
|
|
3a92760058 | |
|
|
031c77257a | |
|
|
31a6137f90 | |
|
|
571fa4d124 | |
|
|
145f664914 | |
|
|
189a5becb7 | |
|
|
70eda57c61 | |
|
|
7ff557b6e7 | |
|
|
5844b7fa74 | |
|
|
cfff794306 | |
|
|
d2e586bd3f | |
|
|
7b84cd63db | |
|
|
3c18d6c1e3 | |
|
|
3f05c1d0ea | |
|
|
16ac615f9b | |
|
|
9a3915e11c | |
|
|
ce1bd8c594 | |
|
|
74c93e2ce4 | |
|
|
a59273de88 | |
|
|
5056129b40 | |
|
|
c89c72bda2 | |
|
|
3050c3bb42 | |
|
|
7f16b8b750 | |
|
|
291e86a669 | |
|
|
4625a13b52 | |
|
|
8827f28ead | |
|
|
d220d3195d | |
|
|
27e35e51ee | |
|
|
3f78131abb | |
|
|
06cd0a02e6 | |
|
|
5d23123cc0 | |
|
|
e3b414ddde | |
|
|
43f1da24e7 | |
|
|
46ad529954 | |
|
|
0f5f14526c | |
|
|
c79d3cb9a9 | |
|
|
d1634b2a58 | |
|
|
29f03a03e7 |
|
|
@ -1,35 +1,36 @@
|
|||
name: pr
|
||||
|
||||
on: [pull_request]
|
||||
on:
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
graphql-14:
|
||||
graphql-15:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile && yarn format:ci
|
||||
- name: Install GraphQL@14.x
|
||||
run: yarn add graphql@^14.5.8
|
||||
- name: Install GraphQL@15.x
|
||||
run: yarn add graphql@^15
|
||||
- name: Test
|
||||
run: yarn -s test:ci --testPathIgnorePatterns v15
|
||||
run: yarn -s test:ci
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x]
|
||||
node-version: [14.x, 16.x]
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
run: yarn --frozen-lockfile || yarn --frozen-lockfile
|
||||
- name: Check Prettier
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: yarn format:ci
|
||||
|
|
@ -37,7 +38,7 @@ jobs:
|
|||
run: yarn -s test:ci
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '10.x'
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '14.x'
|
||||
with:
|
||||
directory: ./coverage
|
||||
|
||||
|
|
@ -55,16 +56,18 @@ jobs:
|
|||
# rm examples/star-wars/star-wars-schema.graphql
|
||||
# node examples/star-wars/dist/schema.js
|
||||
# git diff --exit-code
|
||||
node-version: [10.x]
|
||||
node-version: [14.x]
|
||||
os: [macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile && yarn format:ci
|
||||
run: yarn --frozen-lockfile || yarn --frozen-lockfile
|
||||
- name: Check Formatting
|
||||
run: yarn format:ci
|
||||
- name: Link
|
||||
run: yarn link
|
||||
- name: Example Install
|
||||
|
|
@ -80,8 +83,8 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
run: yarn --frozen-lockfile || yarn --frozen-lockfile
|
||||
- name: Prettier
|
||||
run: yarn format:ci
|
||||
|
|
|
|||
|
|
@ -5,40 +5,40 @@ on:
|
|||
branches: [main]
|
||||
|
||||
jobs:
|
||||
graphql-14:
|
||||
graphql-15:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Install GraphQL@14.x
|
||||
run: yarn add graphql@^14.5.8
|
||||
run: yarn --frozen-lockfile && yarn format:ci
|
||||
- name: Install GraphQL@15.x
|
||||
run: yarn add graphql@^15
|
||||
- name: Test
|
||||
run: yarn -s test:ci --testPathIgnorePatterns v15
|
||||
run: yarn -s test:ci
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x]
|
||||
node-version: [14.x, 16.x]
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
run: yarn --frozen-lockfile || yarn --frozen-lockfile
|
||||
- name: Check Prettier
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: yarn format:ci
|
||||
- name: Test
|
||||
run: yarn -s test:ci
|
||||
- name: Upload coverage to Codecov
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '10.x'
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '14.x'
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
directory: ./coverage
|
||||
|
|
@ -57,16 +57,18 @@ jobs:
|
|||
# rm examples/star-wars/star-wars-schema.graphql
|
||||
# node examples/star-wars/dist/schema.js
|
||||
# git diff --exit-code
|
||||
node-version: [10.x]
|
||||
node-version: [14.x]
|
||||
os: [macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile && yarn format:ci
|
||||
run: yarn --frozen-lockfile || yarn --frozen-lockfile
|
||||
- name: Check Formatting
|
||||
run: yarn format:ci
|
||||
- name: Link
|
||||
run: yarn link
|
||||
- name: Example Install
|
||||
|
|
@ -83,9 +85,9 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
run: yarn --frozen-lockfile || yarn --frozen-lockfile
|
||||
- name: Release Canary
|
||||
env:
|
||||
NPM_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ examples/*/dist
|
|||
website/static/playground-dist
|
||||
yarn-error.log
|
||||
coverage/*
|
||||
tests/esm/out/
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(c) 2018-2019 Tim Griesser
|
||||
(c) 2018-2022 Tim Griesser
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Nexus
|
||||
|
||||

|
||||
[](https://badge.fury.io/js/%40nexus%2Fschema)
|
||||
[](https://github.com/graphql-nexus/nexus/actions/workflows/trunk.yml)
|
||||
[](https://badge.fury.io/js/nexus)
|
||||
|
||||
Declarative, code-first and strongly typed GraphQL schema construction for TypeScript & JavaScript.
|
||||
|
||||
|
|
@ -74,4 +74,4 @@ You can find the docs for Nexus [here](http://nexusjs.org/).
|
|||
|
||||
## Migrate from SDL
|
||||
|
||||
If you've been following an [SDL-first](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3/) approach to build your GraphQL server and want to see what your code looks like when written with GraphQL Nexus, you can use the [**SDL converter**](https://nexus.js.org/converter).
|
||||
If you've been following an [SDL-first](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3/) approach to build your GraphQL server and want to see what your code looks like when written with GraphQL Nexus, you can use the [**SDL converter**](https://nexusjs.org/converter).
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ Welcome to _the_ Nexus tutorial! If you're new to Nexus and want a guided learni
|
|||
Throughout this journey, we'll be making a few minor assumptions about your toolchain to keep the content flowing:
|
||||
|
||||
- Using `npm` as the package manager.
|
||||
- Using a \*nix OS
|
||||
- Using VSCode
|
||||
|
||||
If you're using another set of tools, like Yarn on Windows with Sublime Text, that's totally fine and welcome! Just know that at you'll occasionally need to adapt minor instructions to your situation.
|
||||
|
|
|
|||
|
|
@ -15,23 +15,23 @@ In this first chapter we're just going to get the bare minimum of a Nexus projec
|
|||
|
||||
Start by creating your project directory, initializing your `package.json`, and adding the needed runtime dependencies.
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
mkdir nexus-tutorial && cd nexus-tutorial
|
||||
npm init -y
|
||||
npm add nexus graphql apollo-server
|
||||
npm install nexus graphql apollo-server
|
||||
```
|
||||
|
||||
> Note: `nexus` works with any GraphQL compliant server. We'll use `apollo-server` in this tutorial, but you're free to use whichever fits your use-case best.
|
||||
|
||||
We'll also need `typescript` and `ts-node-dev` as dev dependencies. `ts-node-dev` will enable you to transpile your TS files on the fly and restart your API on changes.
|
||||
|
||||
```bash-symbol
|
||||
npm add --save-dev typescript ts-node-dev
|
||||
```bash-symbol copy
|
||||
npm install --save-dev typescript ts-node-dev
|
||||
```
|
||||
|
||||
To properly get full advantage of TypeScript, we'll need a `tsconfig.json` file. Create one at the root of your project and copy paste the following
|
||||
|
||||
```json
|
||||
```json file=tsconfig.json copy
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2018",
|
||||
|
|
@ -52,10 +52,10 @@ Let's finally add some npm scripts to simplify our future workflows
|
|||
|
||||
<tab>
|
||||
|
||||
```json
|
||||
```json file=package.json
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
+ "dev": "ts-node-dev --transpile-only --no-notify api/app.ts",
|
||||
+ "dev": "ts-node-dev --transpile-only --no-notify api/index.ts",
|
||||
+ "build": "tsc"
|
||||
}
|
||||
```
|
||||
|
|
@ -64,12 +64,12 @@ Let's finally add some npm scripts to simplify our future workflows
|
|||
|
||||
<tab>
|
||||
|
||||
```json
|
||||
```json copy
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "ts-node-dev --transpile-only --no-notify api/app.ts",
|
||||
"dev": "ts-node-dev --transpile-only --no-notify api/index.ts",
|
||||
"build": "tsc"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
|
@ -80,13 +80,13 @@ Let's finally add some npm scripts to simplify our future workflows
|
|||
|
||||
We'll now create our first module at `api/schema.ts`:
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
mkdir api && touch api/schema.ts
|
||||
```
|
||||
|
||||
We'll then setup `nexus` to create an empty schema
|
||||
|
||||
```ts
|
||||
```ts copy
|
||||
// api/schema.ts
|
||||
import { makeSchema } from 'nexus'
|
||||
import { join } from 'path'
|
||||
|
|
@ -106,9 +106,9 @@ export const schema = makeSchema({
|
|||
|
||||
Finally, we'll setup the GraphQL server. We'll intentionally separate the server instantiation from the server listening to make testing easier later.
|
||||
|
||||
Create an `api/server.ts` file and add the following code to instantiate your GraphQL server
|
||||
Create `api/server.ts` and `api/index.ts` files and add the following code to instantiate your GraphQL server:
|
||||
|
||||
```ts
|
||||
```ts copy
|
||||
// api/server.ts
|
||||
import { ApolloServer } from 'apollo-server'
|
||||
import { schema } from './schema'
|
||||
|
|
@ -116,10 +116,8 @@ import { schema } from './schema'
|
|||
export const server = new ApolloServer({ schema })
|
||||
```
|
||||
|
||||
Then create an `api/app.ts` file and add the following to make the server listening
|
||||
|
||||
```ts
|
||||
// api/app.ts
|
||||
```ts copy
|
||||
// api/index.ts
|
||||
import { server } from './server'
|
||||
|
||||
server.listen().then(({ url }) => {
|
||||
|
|
@ -131,7 +129,7 @@ server.listen().then(({ url }) => {
|
|||
|
||||
Ok, with our entrypoint setup, let's boot up dev mode and see what happens.
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
npm run dev
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Your modeling work is going to start on the API layer as opposed to the database
|
|||
|
||||
Create a new module for your Post object at `api/graphql/Post.ts`. We _could_ write our whole schema within say `api/schema.ts` or `api/graphql.ts`, but modularizing your GraphQL type definitions can help scale your codebase. Neither approach is inherently wrong though, so do as you see you fit. For this tutorial we'll use the modular style.
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
mkdir api/graphql && touch api/graphql/Post.ts
|
||||
```
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ type Query {
|
|||
1. The Query object is a central place in your schema where many other types will appear. Like before with the modular GraphQL types decision we again can decide to be modular here. We could either create a new `api/graphql/Query.ts` module (not modular), or we could _collocate_ the exposure of Post object with its definition in `api/graphql/Post.ts` (modular). Staying consistent with before, we'll take the modular way.
|
||||
1. To achieve collocation in Nexus we'll use `schema.extendType`. Its API is _very_ similar to `schema.objectType` with the difference that the defined fields are merged into the _targeted_ type.
|
||||
1. `.nonNull` specifies that clients will always get a value for this field. By default, in Nexus, all "output types" (types returned by fields) are nullable. This is for [best practice reasons](https://graphql.org/learn/best-practices/#nullability). In this case though we indeed want to guarantee that a list will always be returned, never `null`.
|
||||
If you're ever dissatisfied with Nexus' defaults, not to worry, [you can change them](https://www.nexusjs.org/#/api/modules/main/exports/settings?id=schemanullableinputs).
|
||||
If you're ever dissatisfied with Nexus' defaults, not to worry, [you can change them](https://nexusjs.org/docs/api/make-schema#nonnulldefaults).
|
||||
1. `.list` augments the field's type spec, making it wrapped by a List type. Here, a `[Post]`.
|
||||
1. The first parameter specifies the field's name, here `drafts`
|
||||
1. `type: 'Post'` specifies what the field's type should be. Here, a `Post`
|
||||
|
|
@ -268,7 +268,7 @@ You can now open up your GraphQL playground and try the following query (left);
|
|||
|
||||
<block>
|
||||
|
||||
```graphql
|
||||
```graphql copy
|
||||
{
|
||||
drafts {
|
||||
id
|
||||
|
|
@ -286,7 +286,7 @@ You can now open up your GraphQL playground and try the following query (left);
|
|||
```json
|
||||
{
|
||||
"data": {
|
||||
"posts": [
|
||||
"drafts": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Nexus",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: 3. Adding mutations to Your API
|
||||
title: 3. Adding mutations to your API
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
|
@ -21,7 +21,7 @@ The GraphQL Context is a plain JavaScript object shared across all resolvers. Yo
|
|||
|
||||
So go ahead and create the database with its type definition.
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
touch api/db.ts
|
||||
```
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ Now to expose it in our GraphQL context there is two things we need to do:
|
|||
|
||||
We'll begin by creating a new module to hold out the context and its type.
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
touch api/context.ts
|
||||
```
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ export const schema = makeSchema({
|
|||
},
|
||||
contextType: { // 1
|
||||
module: join(__dirname, "./context.ts"), // 2
|
||||
export: "ContextModule", // 3
|
||||
export: "Context", // 3
|
||||
},
|
||||
})
|
||||
```
|
||||
|
|
@ -237,7 +237,6 @@ As before we will take the collocation approach.
|
|||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
// api/graphql/Post.ts
|
||||
// ...
|
||||
|
||||
export const PostMutation = extendType({
|
||||
type: 'Mutation',
|
||||
|
|
@ -310,7 +309,6 @@ export const PostMutation = extendType({
|
|||
|
||||
<tab>
|
||||
|
||||
|
||||
```ts
|
||||
import { objectType, extendType, stringArg, nonNull } from 'nexus'
|
||||
|
||||
|
|
@ -516,7 +514,7 @@ Great, now head on over to the GraphQL Playground and run this query (left). If
|
|||
|
||||
<block>
|
||||
|
||||
```graphql
|
||||
```graphql copy
|
||||
mutation {
|
||||
publish(draftId: 1) {
|
||||
id
|
||||
|
|
@ -555,7 +553,7 @@ Now, that published draft should be visible via the `posts` Query. Run this quer
|
|||
|
||||
<block>
|
||||
|
||||
```graphql
|
||||
```graphql copy
|
||||
query {
|
||||
posts {
|
||||
id
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ title: 4. Testing your API
|
|||
|
||||
## Overview
|
||||
|
||||
So far you've been validating your work by manual interacting with the Playground. That might be reasonable at first (depending on your relationship to TDD) but it will not scale. At some point you are going to want automated testing. So in this chapter you're going to add some automated tests to your e-commerce project. You'll learn about:
|
||||
|
||||
- Nexus' approach to testing
|
||||
- Setting up a test environment
|
||||
- The `nexus/testing` module
|
||||
So far you've been validating your work by manual interacting with the Playground. That might be reasonable at first (depending on your relationship to TDD) but it will not scale. At some point you are going to want automated testing. So in this chapter you're going to add some automated tests to your e-commerce project.
|
||||
|
||||
## It's at the system level
|
||||
|
||||
|
|
@ -28,8 +24,8 @@ During this tutorial, you'll use the [Jest testing framework](https://jestjs.io/
|
|||
|
||||
First, install `jest` and accompanying tools.
|
||||
|
||||
```bash-symbol
|
||||
npm add --save-dev jest @types/jest ts-jest graphql-request get-port
|
||||
```bash-symbol copy
|
||||
npm install --save-dev jest @types/jest ts-jest graphql-request get-port@5.1.1
|
||||
```
|
||||
|
||||
Then, configure jest and npm scripts in your `package.json`
|
||||
|
|
@ -40,7 +36,7 @@ Then, configure jest and npm scripts in your `package.json`
|
|||
|
||||
```json
|
||||
"scripts": {
|
||||
"dev": "ts-node-dev --transpile-only --no-notify api/app",
|
||||
"dev": "ts-node-dev --transpile-only --no-notify api/index.ts",
|
||||
"build": "tsc",
|
||||
+ "generate": "ts-node --transpile-only api/schema",
|
||||
+ "test": "npm run generate && jest"
|
||||
|
|
@ -62,7 +58,7 @@ Then, configure jest and npm scripts in your `package.json`
|
|||
|
||||
```json
|
||||
"scripts": {
|
||||
"dev": "ts-node-dev --transpile-only --no-notify api/app",
|
||||
"dev": "ts-node-dev --transpile-only --no-notify api/index.ts",
|
||||
"build": "tsc",
|
||||
"generate": "ts-node --transpile-only api/schema",
|
||||
"test": "npm run generate && jest"
|
||||
|
|
@ -75,7 +71,7 @@ Then, configure jest and npm scripts in your `package.json`
|
|||
}
|
||||
},
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
|
@ -84,7 +80,7 @@ Then, configure jest and npm scripts in your `package.json`
|
|||
|
||||
Finally, create a `tests` folder at the root of your project and a `Post.test.ts` file inside it
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
mkdir tests && touch tests/Post.test.ts
|
||||
```
|
||||
|
||||
|
|
@ -92,11 +88,11 @@ mkdir tests && touch tests/Post.test.ts
|
|||
|
||||
To ease testing, we'll create a small utility that we'll call `createTestContext`, which is designed for running integration tests.
|
||||
|
||||
When run, it will boot your app in the same process as the test suite and expose an interface for your tests to interact with it. Jest runs each test suite in its own process, so if you have have say eight test suites running in parallel that means you'll have eight app processes running too.
|
||||
When run, it will boot your app in the same process as the test suite and expose an interface for your tests to interact with it. Jest runs each test suite in its own process, so if you have say eight test suites running in parallel that means you'll have eight app processes running too.
|
||||
|
||||
Create a `tests/__helpers.ts` module with the following contents.
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
touch tests/__helpers.ts
|
||||
```
|
||||
|
||||
|
|
@ -256,13 +252,13 @@ it('ensures that a draft can be created and published', async () => {
|
|||
|
||||
1. The test context exposes a GraphQL client at `ctx.client.request` that will help us run operations against our API. Here We're using it to send a publish mutation.
|
||||
2. This is the mutation from the end of last chapter.
|
||||
3. The result will be snapshoted inline allowing us to see the input and output collocated!
|
||||
3. The result will be snapshotted inline allowing us to see the input and output collocated!
|
||||
|
||||
## Try it out
|
||||
|
||||
Now run your tests and let's see the snapshots come to life! It should look similar to this:
|
||||
|
||||
```bash-symbol
|
||||
```bash-symbol copy
|
||||
npm run test
|
||||
```
|
||||
|
||||
|
|
@ -285,7 +281,7 @@ expect(result).toMatchInlineSnapshot(`
|
|||
Published draft snapshot
|
||||
|
||||
```ts
|
||||
// Snapshot that draft and expect `published` to be false
|
||||
// Snapshot that draft and expect `published` to be true
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
+ Object {
|
||||
+ "publish": Object {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,10 @@ title: 5. Persisting data (via Prisma)
|
|||
|
||||
## Overview
|
||||
|
||||
So far we have been working with in-memory data while we learn about other parts of Nexus in a focused manner, but in this chapter we're going to put the focus squarely on data and show how Nexus can be used with a database. This marks an important step toward your blog app becoming more real. You'll learn about:
|
||||
So far we have been working with in-memory data while we learn about other parts of Nexus in a focused manner, but in this chapter we're going to put the focus squarely on data and show how Nexus can be used with a database. This marks an important step toward your blog app becoming more real.
|
||||
|
||||
- Prisma
|
||||
- Setting up a Postgres database locally
|
||||
|
||||
We're going to be using a database called Postgres and a tool called Prisma to interact with it.
|
||||
|
||||
Postgres is a well known open-source relational database. Prisma is a new way of working with databases that we'll learn more about in a moment.
|
||||
In this section, you'll learn how to add Prisma to your GraphQL API.
|
||||
Prisma is a new way of working with databases that we'll learn more about in a moment.
|
||||
|
||||
Its important to understand that Nexus does not _require_ these technology choices and _could_ actually be used with any database and abstractions over them (raw SQL, query builder, ORM..). However, Nexus is built by a team at Prisma (the company) and unsurprisingly there is great integration between its tools and Nexus.
|
||||
|
||||
|
|
@ -20,69 +16,51 @@ Its important to understand that Nexus does not _require_ these technology choic
|
|||
So, what _is_ Prisma? It is an open source database toolkit that consists of the following parts:
|
||||
|
||||
- **Prisma Client**: Auto-generated and type-safe query builder for Node.js & TypeScript
|
||||
- **Prisma Migrate** (experimental): Declarative data modeling & migration system
|
||||
- **Prisma Migrate** (Preview): Declarative data modeling & migration system
|
||||
- **Prisma Studio**: GUI to view and edit data in your database
|
||||
|
||||
At the heart of Prisma is the _Prisma Schema,_ a file usually called `schema.prisma`, that you will see later in this tutorial. It is a declarative file wherein using a domain specific language you encode your database schema, connection to the database, and more.
|
||||
Prisma currently supports 4 relational databases: MySQL, PostgreSQL, SQL Server and SQLite.
|
||||
|
||||
Prisma has great [docs](https://www.prisma.io/docs/understand-prisma/introduction) so definitely check them out at some point. For now you can stay in the flow of this tutorial if you want though. We're going to focus on Prisma Client.
|
||||
|
||||
## Connect to your database
|
||||
## Set up Prisma
|
||||
|
||||
Now that you know a bit about Prisma, let's get going! Do the following:
|
||||
|
||||
- Install the Prisma Client & the Prisma CLI
|
||||
- Install the Prisma client and CLI
|
||||
- Use it in your `api/schema.ts` module
|
||||
- Create your Prisma Schema
|
||||
- Create a `.env` file to store your database credentials
|
||||
- Connect to your database
|
||||
|
||||
like so:
|
||||
|
||||
```bash-symbol
|
||||
npm add @prisma/client && npm add --save-dev @prisma/cli
|
||||
```bash-symbol copy
|
||||
npm install @prisma/client
|
||||
npm install --save-dev prisma
|
||||
```
|
||||
|
||||
```bash-symbol
|
||||
Next, add Prisma to your project by creating your [Prisma schema](https://www.prisma.io/docs/concepts/components/prisma-schema/) file with the following command:
|
||||
|
||||
```bash-symbol copy
|
||||
npx prisma init
|
||||
```
|
||||
|
||||
Almost done, but we still need to setup a Postgres database for our app to connect to. There are a ton of ways to do this so we're just going to show the most straight forward cross-platform way we know how. First, make sure you have [docker installed](https://docs.docker.com/get-docker/). Then, simply run this command:
|
||||
The command creates a new directory called `prisma` which will contain a `schema.prisma` file and add a `.env` file at the root of your project.
|
||||
|
||||
```bash-symbol
|
||||
docker run --detach --publish 5432:5432 -e POSTGRES_PASSWORD=postgres --name nexus-tutorial-postgres postgres:10
|
||||
```
|
||||
For ease of set up, this guide will use SQLite. If you wish to use your preferred database, you can make the switch by simply changing the `provider` and `url` in your `schema.prisma` file.
|
||||
|
||||
That's it. You now have a Postgres server running.
|
||||
|
||||
If you prefer setting up your local Postgres another way go for it. If our suggest approach doesn't work for you, then checkout a few other approaches listed on the [Nexus recipes page](https://www.nexusjs.org/#/references/recipes?id=setting-up-postgresql).
|
||||
|
||||
Finally, in the `prisma/.env` file you've created before, replace `<postgres_connection_url>` with your actual database URL.
|
||||
|
||||
<TabbedContent tabs={['Diff', 'Code']}>
|
||||
<tab>
|
||||
|
||||
```diff
|
||||
-DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
|
||||
+DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp"
|
||||
```
|
||||
|
||||
</tab>
|
||||
<tab>
|
||||
To connect to your database, you'll need to set the `url` field in the `datasource` block in your Prisma schema.
|
||||
Since the `url` is set via an environment variable, you will define it in `.env`.
|
||||
|
||||
```diff
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp"
|
||||
```
|
||||
|
||||
|
||||
</tab>
|
||||
</TabbedContent>
|
||||
|
||||
<!-- **TODO: find a way to make sure their connection url is working**
|
||||
Confirm things are setup correctly by ...
|
||||
-->
|
||||
|
||||
## Create your database schema
|
||||
## Create your first database model
|
||||
|
||||
It is now time to replace our in-memory data with actual tables in our database. To do this we'll write models in our Prisma Schema.
|
||||
|
||||
|
|
@ -91,12 +69,12 @@ In chapters 2 and 3 we already began to model our blog domain with the GraphQL t
|
|||
<TabbedContent tabs={['Diff', 'Code']}>
|
||||
<tab>
|
||||
|
||||
```groovy diff
|
||||
```prisma diff
|
||||
// prisma/schema.prisma
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
+ provider = "sqlite"
|
||||
+ url = "file:./dev.db"
|
||||
}
|
||||
|
||||
generator client {
|
||||
|
|
@ -114,12 +92,12 @@ generator client {
|
|||
</tab>
|
||||
<tab>
|
||||
|
||||
```groovy
|
||||
```prisma
|
||||
// prisma/schema.prisma
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
provider = "sqlite"
|
||||
url = "file:./dev.db"
|
||||
}
|
||||
|
||||
generator client {
|
||||
|
|
@ -139,10 +117,13 @@ model Post {
|
|||
|
||||
With our database schema specified, we're now ready to proceed to our first database migration! To do that, we'll use the Prisma CLI.
|
||||
|
||||
```bash-symbold
|
||||
npx prisma migrate dev --preview-feature
|
||||
```bash-symbol copy
|
||||
npx prisma migrate dev --name init --preview-feature
|
||||
```
|
||||
|
||||
This will create a database migration called `init`. Once a migration is complete, the Prisma CLI will create `dev.db` database and apply the changes against your database.
|
||||
Once the first migration is complete, the Prisma CLI will install `@prisma/client` package. In subsequent migrations, Prisma CLI will generate the Prisma Client.
|
||||
|
||||
## Access your database
|
||||
|
||||
Now let's finally ditch our in-memory data! Let's replace it with the Prisma Client
|
||||
|
|
@ -154,7 +135,7 @@ Now let's finally ditch our in-memory data! Let's replace it with the Prisma Cli
|
|||
// api/db.ts
|
||||
+import { PrismaClient } from '@prisma/client'
|
||||
|
||||
export const db = new PrismaClient()
|
||||
+export const db = new PrismaClient()
|
||||
|
||||
-export interface Post {
|
||||
- id: number
|
||||
|
|
@ -175,7 +156,7 @@ export const db = new PrismaClient()
|
|||
</tab>
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
```ts copy
|
||||
// api/db.ts
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
|
|
@ -209,7 +190,7 @@ export const context = {
|
|||
</tab>
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
```ts copy
|
||||
// api/context.ts
|
||||
import { db } from "./db";
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
|
@ -226,11 +207,6 @@ export const context = {
|
|||
</tab>
|
||||
</TabbedContent>
|
||||
|
||||
Finally, generate the PrismaClient using the Prisma CLI
|
||||
|
||||
```bash-symbol
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
Let's now replace all our previous in-memory db interactions with calls to the Prisma Client
|
||||
|
||||
|
|
@ -290,15 +266,15 @@ export const PostMutation = extendType({
|
|||
draftId: nonNull(intArg()),
|
||||
},
|
||||
resolve(_root, args, ctx) {
|
||||
- let postToPublish = ctx.db.posts.find((p) => p.id === args.draftId)
|
||||
- let draftToPublish = ctx.db.posts.find((p) => p.id === args.draftId)
|
||||
|
||||
- if (!postToPublish) {
|
||||
- if (!draftToPublish) {
|
||||
- throw new Error('Could not find draft with id ' + args.draftId)
|
||||
- }
|
||||
|
||||
- postToPublish.published = true
|
||||
- draftToPublish.published = true
|
||||
|
||||
- return postToPublish
|
||||
- return draftToPublish
|
||||
|
||||
+ return ctx.db.post.update({
|
||||
+ where: { id: args.draftId },
|
||||
|
|
@ -315,7 +291,7 @@ export const PostMutation = extendType({
|
|||
</tab>
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
```ts copy
|
||||
// api/graphql/Post.ts
|
||||
// ...
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ There's a couple of things you'll have to do in order to run integration tests a
|
|||
|
||||
## How does it work?
|
||||
|
||||
To perform integration testing against a real database, here are the high level steps we will follow _for every tests_:
|
||||
To perform integration testing against a real database, here are the high level steps we will follow _for every test_:
|
||||
|
||||
- Connect to a Postgres database. Most likely your dev database.
|
||||
- Connect to a SQLite database. Most likely your dev database.
|
||||
- Migrate our database schema to a randomly generated schema of that database. This ensures that every tests runs from a clean un-seeded database
|
||||
- Make the Prisma Client connect to that Postgres schema
|
||||
- Make the Prisma Client connect to that SQLite database
|
||||
- Run your test
|
||||
- Teardown the schema entirely
|
||||
|
||||
|
|
@ -23,10 +23,10 @@ To perform integration testing against a real database, here are the high level
|
|||
|
||||
To achieve some of the steps described above, we'll tweak our test context.
|
||||
|
||||
First, install the `pg` and `nanoid` packages
|
||||
First, install the `sqlite3` and `nanoid` packages
|
||||
|
||||
```bash
|
||||
npm add --save-dev pg @types/pg nanoid
|
||||
```bash copy
|
||||
npm install --save-dev sqlite3 @types/sqlite3
|
||||
```
|
||||
|
||||
Then, head to your `tests/__helpers.ts` file to add the following imports and code
|
||||
|
|
@ -41,9 +41,8 @@ import { ServerInfo } from "apollo-server";
|
|||
+import { execSync } from "child_process";
|
||||
import getPort, { makeRange } from "get-port";
|
||||
import { GraphQLClient } from "graphql-request";
|
||||
+import { nanoid } from "nanoid";
|
||||
+import { join } from "path";
|
||||
+import { Client } from "pg";
|
||||
+import { Database } from "sqlite3";
|
||||
+import { db } from "../api/db";
|
||||
import { server } from "../api/server";
|
||||
|
||||
|
|
@ -98,42 +97,24 @@ function graphqlTestContext() {
|
|||
|
||||
+function prismaTestContext() {
|
||||
+ const prismaBinary = join(__dirname, "..", "node_modules", ".bin", "prisma");
|
||||
+ let schema = "";
|
||||
+ let databaseUrl = "";
|
||||
+ let prismaClient: null | PrismaClient = null;
|
||||
+
|
||||
+ return {
|
||||
+ async before() {
|
||||
+ // Generate a unique schema identifier for this test context
|
||||
+ schema = `test_${nanoid()}`;
|
||||
+ // Generate the pg connection string for the test schema
|
||||
+ databaseUrl = `postgres://postgres:postgres@localhost:5432/testing?schema=${schema}`;
|
||||
+
|
||||
+ // Set the required environment variable to contain the connection string
|
||||
+ // to our database test schema
|
||||
+ process.env.DATABASE_URL = databaseUrl;
|
||||
+
|
||||
+ // Run the migrations to ensure our schema has the required structure
|
||||
+ execSync(`${prismaBinary} db push --preview-feature`, {
|
||||
+ env: {
|
||||
+ ...process.env,
|
||||
+ DATABASE_URL: databaseUrl,
|
||||
+ },
|
||||
+ });
|
||||
+ execSync(`${prismaBinary} db push --preview-feature`,);
|
||||
+
|
||||
+ // Construct a new Prisma Client connected to the generated Postgres schema
|
||||
+ // Construct a new Prisma Client connected to the generated schema
|
||||
+ prismaClient = new PrismaClient();
|
||||
+
|
||||
+ return prismaClient;
|
||||
+ },
|
||||
+ async after() {
|
||||
+ // Drop the schema after the tests have completed
|
||||
+ const client = new Client({
|
||||
+ connectionString: databaseUrl,
|
||||
+ });
|
||||
+ await client.connect();
|
||||
+ await client.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
|
||||
+ await client.end();
|
||||
+ const client = new Database(':memory:');
|
||||
|
||||
+ await client.close();
|
||||
+
|
||||
+ // Release the Prisma Client connection
|
||||
+ await prismaClient?.$disconnect();
|
||||
|
|
@ -145,16 +126,15 @@ function graphqlTestContext() {
|
|||
</tab>
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
```ts copy
|
||||
// tests/__helpers.ts
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { ServerInfo } from "apollo-server";
|
||||
import { execSync } from "child_process";
|
||||
import getPort, { makeRange } from "get-port";
|
||||
import { GraphQLClient } from "graphql-request";
|
||||
import { nanoid } from "nanoid";
|
||||
import { join } from "path";
|
||||
import { Client } from "pg";
|
||||
import { Database } from "sqlite3";
|
||||
import { db } from "../api/db";
|
||||
import { server } from "../api/server";
|
||||
|
||||
|
|
@ -209,42 +189,23 @@ function graphqlTestContext() {
|
|||
|
||||
function prismaTestContext() {
|
||||
const prismaBinary = join(__dirname, "..", "node_modules", ".bin", "prisma");
|
||||
let schema = "";
|
||||
let databaseUrl = "";
|
||||
let prismaClient: null | PrismaClient = null;
|
||||
|
||||
return {
|
||||
async before() {
|
||||
// Generate a unique schema identifier for this test context
|
||||
schema = `test_${nanoid()}`;
|
||||
// Generate the pg connection string for the test schema
|
||||
databaseUrl = `postgres://postgres:postgres@localhost:5432/testing?schema=${schema}`;
|
||||
|
||||
// Set the required environment variable to contain the connection string
|
||||
// to our database test schema
|
||||
process.env.DATABASE_URL = databaseUrl;
|
||||
|
||||
// Run the migrations to ensure our schema has the required structure
|
||||
execSync(`${prismaBinary} db push --preview-feature`, {
|
||||
env: {
|
||||
...process.env,
|
||||
DATABASE_URL: databaseUrl,
|
||||
},
|
||||
});
|
||||
execSync(`${prismaBinary} db push --preview-feature`,);
|
||||
|
||||
// Construct a new Prisma Client connected to the generated Postgres schema
|
||||
// Construct a new Prisma Client connected to the generated schema
|
||||
prismaClient = new PrismaClient();
|
||||
|
||||
return prismaClient;
|
||||
},
|
||||
async after() {
|
||||
// Drop the schema after the tests have completed
|
||||
const client = new Client({
|
||||
connectionString: databaseUrl,
|
||||
});
|
||||
await client.connect();
|
||||
await client.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
|
||||
await client.end();
|
||||
const client = new Database(':memory:');
|
||||
|
||||
await client.close();
|
||||
|
||||
// Release the Prisma Client connection
|
||||
await prismaClient?.$disconnect();
|
||||
|
|
@ -258,12 +219,10 @@ function prismaTestContext() {
|
|||
|
||||
The `prismaTestContext` is in charge of a couple of things:
|
||||
|
||||
1. Push a randomly generated schema into the database before each test. This ensure that your tests can add data to an "isolated" and clean database.
|
||||
1. Flush the changed schema after each test
|
||||
1. Add an instance of a Prisma Client connected to the schema specifically for the test
|
||||
|
||||
Make sure that the `databaseUrl` variable has the right credentials to connect to your own database.
|
||||
Leave the `/testing?schema=...` part though. This ensures, once again, that your tests will add data to your Postgres instance in a separate database called `testing` in a schema that randomly generated.
|
||||
1. Connect to an in-memory instance of the SQLite database
|
||||
2. Pushes the Prisma Schema to the database
|
||||
3. Generates a new Prisma Client
|
||||
4. Add an instance of a Prisma Client connected to the schema specifically for the test
|
||||
|
||||
## Updating our test
|
||||
|
||||
|
|
@ -338,4 +297,4 @@ expect(persistedData).toMatchInlineSnapshot(`
|
|||
|
||||
Congrats, you've performed your first real-world integration test. The fact that integration tests are completely decoupled from the implementation of your GraphQL API makes it a lot easier to maintain your test suite as you evolve your API. What matters is only the data that it produces, which also helps you cover your app a lot more than a single unit test.
|
||||
|
||||
This is the end of the tutorial. Thanks for trying it out and please share any feedback you have with us!
|
||||
This is the end of the tutorial. Thanks for trying it out and please share any feedback you have with us!
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ const Post = objectType({
|
|||
const Query = queryType({
|
||||
definition(t) {
|
||||
t.list.field('posts', {
|
||||
type: "Post",
|
||||
resolve: () => [
|
||||
{
|
||||
id: '1',
|
||||
|
|
@ -93,7 +94,7 @@ There are numerous benefits to taking a code-first approach with Nexus:
|
|||
|
||||
When building a schema-first GraphQL API, it is common to start out by placing all type definitions and resolvers in a single file. When both the schema and the resolvers live next to one another, it's fairly straightforward to work in both at the same time.
|
||||
|
||||
As the application grows, however, it is most often desired to move parts of the schema into their own separate modules and files. It's at this point that working on a GraphQL API becomes a bit more tedious. With this modularization comes the need to switch back and forth between the Schema Definition Language and JavaScript/TypeScript to write the resolvers. Not only does one need to constantly switch between files, they also need to do a context switch mentally to work between the two langauges.
|
||||
As the application grows, however, it is most often desired to move parts of the schema into their own separate modules and files. It's at this point that working on a GraphQL API becomes a bit more tedious. With this modularization comes the need to switch back and forth between the Schema Definition Language and JavaScript/TypeScript to write the resolvers. Not only does one need to constantly switch between files, they also need to do a context switch mentally to work between the two languages.
|
||||
|
||||
With Nexus, our schema and its resolvers are always defined together. Nexus also allows us to write everything in a common language. This allows us to side-step the co-location/context switching issue altogether and helps us to be more productive, even as our applications grow to be quite large.
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ A downside of the schema-first approach is the need to repeat yourself in schema
|
|||
|
||||
### Defining enums
|
||||
|
||||
When defining an enum using the schema-first approach, the enum must first be defined in the schema definition languange:
|
||||
When defining an enum using the schema-first approach, the enum must first be defined in the schema definition language:
|
||||
|
||||
```graphql
|
||||
enum UserRole {
|
||||
|
|
|
|||
|
|
@ -134,9 +134,9 @@ objectType({
|
|||
definition(t) {
|
||||
t.id('a')
|
||||
t.list.id('b')
|
||||
t.nullable.list.id('c')
|
||||
t.list.nullable.id('d')
|
||||
t.nullable.list.nullable.id('e')
|
||||
t.nonNull.list.id('c')
|
||||
t.list.nonNull.id('d')
|
||||
t.nonNull.list.nonNull.id('e')
|
||||
},
|
||||
})
|
||||
```
|
||||
|
|
@ -144,10 +144,10 @@ objectType({
|
|||
```graphql
|
||||
type Alpha {
|
||||
a: ID
|
||||
b: [ID!]!
|
||||
c: [ID!]
|
||||
d: [ID]!
|
||||
e: [ID]
|
||||
b: [ID]
|
||||
c: [ID]!
|
||||
d: [ID!]
|
||||
e: [ID!]!
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -283,4 +283,4 @@ However, the data that flows through your resolvers can be completely different
|
|||
|
||||
If that's the case, you will need to tell Nexus what the shape of this data is thanks to the ***Source Types***.
|
||||
|
||||
[You can read more about them here](/guides/source-types).
|
||||
[You can read more about them here](/guides/source-types).
|
||||
|
|
|
|||
|
|
@ -232,13 +232,13 @@ const Query = queryType({
|
|||
})
|
||||
```
|
||||
|
||||
In a serious/large application with a model layer in the codebase its likely this kind of logic would not live in your resolvers at all.
|
||||
In a serious/large application with a model layer in the codebase it's likely this kind of logic would not live in your resolvers at all.
|
||||
|
||||
Like with the Centralized strategy Nexus leverages TypeScript to ensure your implementation is correct.
|
||||
|
||||
1. The resolver return type for fields whose type is a union will ensure all returned data includes a `__typename` field.
|
||||
2. For a given union type, if all fields that are typed as it have their resolvers returning data with `__typename` then back on the union type `resolveType` will be optional.
|
||||
3. Nexus is smart about what it needs to be satisfied by `__typename` presence. Rather than being a requirement of the model type, it is a requirement of the model type in resolver cases under the union type. For example note below how the `Photo` model type is not required to include `__typename` under the `photos` Query type field:
|
||||
3. Nexus is smart about when it needs to be satisfied by `__typename` presence. Rather than being a requirement of the model type, it is a requirement of the model type in resolver cases under the union type. For example note below how the `Photo` model type is not required to include `__typename` under the `photos` Query type field:
|
||||
|
||||
```ts
|
||||
const Query = queryType({
|
||||
|
|
@ -269,7 +269,7 @@ Like with the Centralized strategy Nexus leverages TypeScript to ensure your imp
|
|||
})
|
||||
```
|
||||
|
||||
Beware that when this strategy is enabled the `abstractTypeRuntimeChecks` feature will automatically be disabled. This is because it is not practical at runtime to find out if resolvers will return objects that include the `__typename` field. This trade-off can be acceptable since the runtime checks are a redundant safety measure over the static type checks. So as long as you are not ignoring static errors related to Nexus' abstract type type checks then you then you should still have a safe implementation.
|
||||
Beware that when this strategy is enabled the `abstractTypeRuntimeChecks` feature will automatically be disabled. This is because it is not practical at runtime to find out if resolvers will return objects that include the `__typename` field. This trade-off can be acceptable since the runtime checks are a redundant safety measure over the static type checks. So as long as you are not ignoring static errors related to Nexus' abstract type type checks then you should still have a safe implementation.
|
||||
|
||||
#### Modular Strategy (`isTypeOf`)
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ makeSchema({
|
|||
})
|
||||
```
|
||||
|
||||
Nexus enables enabling/disabling strategies because having them all enabled at can lead to a confusing excess of type errors when there is an invalid implementation of an abstract type. Nexus doesn't force you to pick only one strategy, however it does consider using multiple strategies sightly more advanced. Refer to the [Multiple Strategies](#multiple-strategies) section for details.
|
||||
Nexus enables enabling/disabling strategies because having them all enabled at can lead to a confusing excess of type errors when there is an invalid implementation of an abstract type. Nexus doesn't force you to pick only one strategy, however it does consider using multiple strategies slightly more advanced. Refer to the [Multiple Strategies](#multiple-strategies) section for details.
|
||||
|
||||
When you customize the strategy settings all strategies become disabled except for those that you opt into. For example in the following:
|
||||
|
||||
|
|
@ -403,7 +403,7 @@ In addition to giving typings for static type checks Nexus also performs runtime
|
|||
|
||||
Runtime checks are automatically enabled. In development they will be a warning but in production they will be a thrown error. This way your development flow will not be unduly interrupted but your e.g. deployment pipeline in CI will be halted helping ensure you ship correct code to your users.
|
||||
|
||||
If ever you need to diable this runtime check you can disable the feature like so:
|
||||
If ever you need to disable this runtime check you can disable the feature like so:
|
||||
|
||||
```ts
|
||||
makeSchema({
|
||||
|
|
@ -432,7 +432,7 @@ Here is a summary of the effects:
|
|||
| DMF & Modular | `*` `__typename` required _unless_ `isTypeOf` implemented on all member types of the abstract type. |
|
||||
| DMF & Modular & centralized | `*` `__typename` required _unless_ `isTypeOf` implemented on all member types of the abstract type _or_ `resolveType` on the abstract type. |
|
||||
|
||||
`*` Nexus cannot require `resolveType`/`isTypeOf` even though it _should_ becuase it cannot statically detect when you have implemented `__typename`.
|
||||
`*` Nexus cannot require `resolveType`/`isTypeOf` even though it _should_ because it cannot statically detect when you have implemented `__typename`.
|
||||
|
||||
Here is a code example:
|
||||
|
||||
|
|
@ -570,7 +570,7 @@ The default `resolveType` implementation is actually to apply the other strategi
|
|||
|
||||
### Interface Types in Theory
|
||||
|
||||
Interfaces allow you to define a set of fields that you can then use across objects in your schema to enforce that they all have the fields defined in the interface. Interfaces also act as a form of polymorphism at runtime. You can make a field's type be an interface and may thus then return any type that implements the interface. To illustrate the point we'll appropiate the schema that we used to show off union types.
|
||||
Interfaces allow you to define a set of fields that you can then use across objects in your schema to enforce that they all have the fields defined in the interface. Interfaces also act as a form of polymorphism at runtime. You can make a field's type be an interface and may thus then return any type that implements the interface. To illustrate the point we'll appropriate the schema that we used to show off union types.
|
||||
|
||||
```graphql
|
||||
interface Media {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,6 @@ A **field type** is the valid return value used to a field on an object type. In
|
|||
|
||||
### Configuring our types
|
||||
|
||||
The [Ghost Example](https://github.com/graphql-nexus/nexus/blob/develop/examples/ghost/src/ghost-schema.ts) is the best to look at for an example of how we're able to capture the types from existing runtime objects or definitions and merge them with our schema.
|
||||
The [Ghost Example](https://github.com/graphql-nexus/nexus/blob/main/examples/ghost/src/ghost-schema.ts) is the best to look at for an example of how we're able to capture the types from existing runtime objects or definitions and merge them with our schema.
|
||||
|
||||
The [makeSchema](/api/make-schema) takes several options which helps us find the types we need to import into our generated schema, and customize where these generated types are output. Read more about the API [here](/api/make-schema).
|
||||
|
|
|
|||
|
|
@ -28,18 +28,3 @@ Each public API is documented below, feel free to open a PR with more examples/c
|
|||
- [extendType / extendInputType](/api/extend-type)
|
||||
- [mutationField](/api/mutation-field)
|
||||
- [queryField](/api/query-field)
|
||||
|
||||
## Resolving: Inline Function
|
||||
|
||||
One common idiom in GraphQL is exposing fields that mask or rename the property name on the backing object. GraphQL Nexus makes this simple by allowing a function as the second parameter to any built-in scalar resolver function.
|
||||
|
||||
```ts
|
||||
const User = objectType({
|
||||
name: 'User',
|
||||
definition(t) {
|
||||
t.id('id', o => o.user_id)
|
||||
t.string('name', o => o.user_name)
|
||||
t.string('description', o => o.user_description)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ codeStyle: true
|
|||
## objectType
|
||||
|
||||
```ts
|
||||
objectType(typeName: string, fn: ObjectDefinitionBlock): NexusObjectType
|
||||
objectType(config: ObjectDefinitionBlock): NexusObjectType
|
||||
```
|
||||
|
||||
The most basic components of a GraphQL schema are object types, a type you can fetch from your schema, with fields:
|
||||
|
|
|
|||
|
|
@ -65,3 +65,14 @@ const SomeObject = objectType({
|
|||
```
|
||||
|
||||
Check the type-definitions or [the examples](https://github.com/graphql-nexus/nexus/tree/main/examples) for a full illustration of the various options for `scalarType`, or feel free to open a PR on the docs to help document!
|
||||
|
||||
|
||||
## Pass scalar to schema generation
|
||||
It's important to list the scalar in the `types` attribute of `makeSchema`
|
||||
|
||||
```ts
|
||||
const schema = makeSchema({
|
||||
types: [GQLDate] // Add Scalar to Array
|
||||
})
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ codeStyle: true
|
|||
|
||||
## interfaceType
|
||||
|
||||
[GraphQL Docs for Interface Types](https://graphql.org/learn/schema/#input-types)
|
||||
[GraphQL Docs for Interface Types](https://graphql.org/learn/schema/#interfaces)
|
||||
|
||||
In Nexus, you do not need to redefine the interface fields on the
|
||||
implementing object types, instead you may use `.implements(interfaceName)`
|
||||
|
|
|
|||
|
|
@ -10,11 +10,31 @@ codeStyle: true
|
|||
Defines a complex object which can be passed as an input value.
|
||||
|
||||
```ts
|
||||
export const InputType = inputObjectType({
|
||||
name: 'InputType',
|
||||
import { extendType, inputObjectType } from 'nexus'
|
||||
|
||||
export const CommentInputType = inputObjectType({
|
||||
name: 'CommentInputType',
|
||||
definition(t) {
|
||||
t.nonNull.string('key')
|
||||
t.int('answer')
|
||||
t.nonNull.int('userId')
|
||||
t.nonNull.string('body')
|
||||
}
|
||||
})
|
||||
|
||||
export const CommentMutation = extendType({
|
||||
type: 'Mutation',
|
||||
definition(t) {
|
||||
t.field('createComment', {
|
||||
type: 'Comment',
|
||||
args: { data: CommentInputType },
|
||||
resolve(_root, args, ctx) {
|
||||
return ctx.prisma.comment.create({
|
||||
data: {
|
||||
user_id: args.userId,
|
||||
body: args.body,
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { queryType, stringArg, list } from 'nexus'
|
|||
queryType({
|
||||
definition(t) {
|
||||
t.field('tags', {
|
||||
type: list('String') // -> [String]
|
||||
type: list('String'), // -> [String]
|
||||
args: {
|
||||
ids: list(stringArg()) // or list('String') -> [String]
|
||||
},
|
||||
|
|
@ -73,7 +73,7 @@ Below are some more usage examples of `nonNull`:
|
|||
| `list(nonNull('String'))` | `[String!]` | `[String!]` |
|
||||
| `nonNull(list(nonNull('String')))` | `[String!]!` | `[String!]!` |
|
||||
| `nonNull(nonNull('String'))` | `String!` | `String!` |
|
||||
| `nonNull(nullable('String'))` | `String! | `String! |
|
||||
| `nonNull(nullable('String'))` | `String!` | `String!` |
|
||||
|
||||
## nullable
|
||||
|
||||
|
|
|
|||
|
|
@ -79,11 +79,6 @@ makeSchema({
|
|||
typeMatch: name => new RegExp(`(?:interface|type|class)\\s+(${name}s?)\\W`, 'g'),
|
||||
},
|
||||
],
|
||||
// Typing for the GraphQL context
|
||||
contextType: {
|
||||
module: '@packages/data-context/src/DataContext.ts',
|
||||
alias: 'ctx',
|
||||
},
|
||||
mapping: {
|
||||
Date: 'Date',
|
||||
DateTime: 'Date',
|
||||
|
|
@ -91,10 +86,15 @@ makeSchema({
|
|||
},
|
||||
debug: false,
|
||||
},
|
||||
// Typing for the GraphQL context
|
||||
contextType: {
|
||||
module: '@packages/data-context/src/DataContext.ts',
|
||||
alias: 'ctx',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The [Ghost Example](https://github.com/graphql-nexus/nexus/blob/develop/examples/ghost/src/ghost-schema.ts) is the best place to look for an example of how we're able to capture the types from existing runtime objects or definitions and merge them with our schema.
|
||||
The [Ghost Example](https://github.com/graphql-nexus/nexus/blob/main/examples/ghost/src/ghost-schema.ts) is the best place to look for an example of how we're able to capture the types from existing runtime objects or definitions and merge them with our schema.
|
||||
|
||||
## shouldExitAfterGenerateArtifacts
|
||||
|
||||
|
|
@ -125,15 +125,34 @@ makeSchema({
|
|||
## nonNullDefaults
|
||||
|
||||
Controls the nullability of the input / output types emitted by `nexus`. The current Nexus default is
|
||||
`{ output: true, input: false }`, though the `graphql-js` / spec default is `{ output: false, input: false }`.
|
||||
`{ output: false, input: false }` same as `graphql-js` spec.
|
||||
`output`: Whether output field (object type fields) types are non-null by default.
|
||||
`input`: Whether input field (field arguments, input object type fields) types are non-null by default.
|
||||
|
||||
You should make a decision on this and supply the option yourself, it may be changed / required in the future.
|
||||
|
||||
Read more on this in the [getting-started](../../../getting-started) guide.
|
||||
|
||||
### typegenConfig, formatTypegen
|
||||
### typegenConfig
|
||||
|
||||
Escape hatches for more advanced cases which need further control over. You typically won't need these.
|
||||
Escape hatch for more advanced cases which need further control over generated files. You typically won't need this.
|
||||
|
||||
### formatTypegen
|
||||
|
||||
Manually apply a formatter to the generated content before saving. Function exposes content and type of generated file.
|
||||
|
||||
```ts
|
||||
makeSchema({
|
||||
// ...
|
||||
formatTypegen: (content, type) => {
|
||||
if (type === 'types') {
|
||||
return `/* eslint-disable */
|
||||
\n ${content}`;
|
||||
}
|
||||
return content;
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### customPrintSchemaFn
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const usersQueryField = queryField('user', {
|
|||
as shorthand for:
|
||||
|
||||
```ts
|
||||
export const createUser = extendType({
|
||||
export const usersQueryField = extendType({
|
||||
type: 'Query',
|
||||
definition(t) {
|
||||
t.field('user', {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ title: Relay Connection
|
|||
|
||||
## Connection Plugin
|
||||
|
||||
The connection plugin provides a new method on the object definition builder, enabling paginated associations between types, following the [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm#sec-Node). It provides simple ways to customize fields available on the `Connection`, `Edges`, or `PageInfo` types.
|
||||
The connection plugin provides a new method on the object definition builder, enabling paginated associations between types, following the [Relay Connection Specification](https://relay.dev/graphql/connections.htm#sec-Node). It provides simple ways to customize fields available on the `Connection`, `Edges`, or `PageInfo` types.
|
||||
|
||||
To install, add the `connectionPlugin` to the `makeSchema.plugins` array, along with any other plugins
|
||||
you'd like to include:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,21 @@ title: Field Authorize
|
|||
|
||||
## Field Authorize
|
||||
|
||||
The authorize plugin allows us to define field-level authorization to a query:
|
||||
The field Authorize Plugin plugin provides a new property on the field config called `authorize`.
|
||||
|
||||
```ts
|
||||
import { makeSchema, fieldAuthorizePlugin } from 'nexus'
|
||||
|
||||
const schema = makeSchema({
|
||||
// ... types, etc,
|
||||
plugins: [
|
||||
// ... other plugins
|
||||
fieldAuthorizePlugin(),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
It allows us to define field-level authorization to a query:
|
||||
|
||||
```ts
|
||||
t.field('postById', {
|
||||
|
|
@ -16,3 +30,5 @@ t.field('postById', {
|
|||
},
|
||||
})
|
||||
```
|
||||
|
||||
`authorize` is a function that provides authorization for an individual field. Returning `true` or `Promise<true>` means the field can be accessed. Returning `false` or `Promise<false>` will respond with a "Not Authorized" error for the field. Returning or throwing an error will also prevent the resolver from executing.
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ title: Overview
|
|||
|
||||
This plugin integrates [Prisma](https://www.prisma.io/) into [Nexus](https://nexusjs.org/). It gives you an API to project fields from models defined in your Prisma schema into your GraphQL API. It also gives you an API to build GraphQL root fields that allow your API clients to query and mutate data.
|
||||
|
||||
> **Note**: The Prisma team is currently [rewriting](https://github.com/graphql-nexus/nexus-plugin-prisma/issues/1039) the plugin to make it maintainable longterm.
|
||||
**Note**: You may also use [`nexus-prisma`](https://github.com/graphql-nexus/nexus-prisma), a newer API for integrating Nexus and Prisma.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash-symbol
|
||||
npm add nexus-plugin-prisma @prisma/client
|
||||
npm add -D @prisma/cli
|
||||
npm install nexus-plugin-prisma @prisma/client
|
||||
npm install -D prisma
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
|
@ -154,7 +154,7 @@ There are two ways you can start with the Prisma plugin. Either from scratch, or
|
|||
3. Create a migration from changes in Prisma schema and run the migration
|
||||
|
||||
```bash-symbol
|
||||
npx prisma migrate dev --preview-feature
|
||||
npx prisma migrate dev
|
||||
```
|
||||
|
||||
You're ready to start working!
|
||||
|
|
@ -205,7 +205,7 @@ Create a `schema.prisma` file and add your database credentials in it so that Pr
|
|||
2. Introspect your database:
|
||||
|
||||
```bash-symbol
|
||||
npm run prisma introspect
|
||||
npx prisma db pull
|
||||
```
|
||||
|
||||
3. Generate the Prisma Client. Add the following block at the top of your `schema.prisma` file:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ tocDepth: 2
|
|||
|
||||
Only available within [`objectType`](/api/object-type) definitions.
|
||||
|
||||
`t.model` contains configurable _field projectors_ that you use for projecting fields of your [Prisma models](https://github.com/prisma/prisma2/blob/master/docs/data-modeling.md#models) onto your [GraphQL Objects](https://graphql.github.io/graphql-spec/June2018/#sec-Objects). The precise behaviour of field projectors vary by the Prisma type being projected. Refer to the respective sub-sections for details.
|
||||
`t.model` contains configurable _field projectors_ that you use for projecting fields of your [Prisma models](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/data-model) onto your [GraphQL Objects](https://graphql.github.io/graphql-spec/June2018/#sec-Objects). The precise behaviour of field projectors vary by the Prisma type being projected. Refer to the respective sub-sections for details.
|
||||
|
||||
### Model-object mapping
|
||||
|
||||
|
|
@ -1586,7 +1586,7 @@ queryType({
|
|||
|
||||
type Query {
|
||||
user(where: UserWhereUniqueInput!): User
|
||||
users(orderBy: UserOrderByInput): [User!]!
|
||||
users(orderBy: [UserOrderByInput!]): [User!]!
|
||||
}
|
||||
|
||||
type Post {
|
||||
|
|
@ -1598,7 +1598,7 @@ type Post {
|
|||
type User {
|
||||
id: Int!
|
||||
name: String!
|
||||
posts(orderBy: UserPostsOrderByInput): [Post!]!
|
||||
posts(orderBy: [UserPostsOrderByInput!]): [Post!]!
|
||||
}
|
||||
|
||||
input UserOrderByInput {
|
||||
|
|
@ -1625,7 +1625,7 @@ enum OrderByArg {
|
|||
|
||||
```graphql
|
||||
query entrypointOrdering {
|
||||
users(orderBy: { name: asc }) {
|
||||
users(orderBy: [{ name: asc }]) {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
|
@ -1633,7 +1633,7 @@ query entrypointOrdering {
|
|||
|
||||
query relationOrdering {
|
||||
user(where: { id: 1643 }) {
|
||||
posts(orderBy: { title: dsc }) {
|
||||
posts(orderBy: [{ title: desc }]) {
|
||||
title
|
||||
body
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ This page describes how you can remove the `nexus-plugin-prisma` from your Graph
|
|||
|
||||
### Uninstall the npm package
|
||||
|
||||
Nexus
|
||||
You can remove the npm package from your project using the following command:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="npm" icon="code"/>, <FileWithIcon text="Yarn" icon="code"/>]}>
|
||||
|
|
@ -61,6 +62,14 @@ export const schema = makeSchema({
|
|||
})
|
||||
```
|
||||
|
||||
### Using the SDL Converter
|
||||
|
||||
Nexus and `nexus-plugin-prisma` generates and emits GraphQL types into a `.graphql` file in your project.
|
||||
Copy and paste the generated types into the [SDL Converter](https://nexusjs.org/converter) to convert your existing SDL into Nexus code.
|
||||
|
||||

|
||||
|
||||
|
||||
## Migrating from `t.model()` to plain Nexus
|
||||
|
||||
`t.model()` is a function that's exposed by the `nexus-plugin-prisma` which allows you to _project_ the field of a Prisma model to your GraphQL API inside of an `objectType` definition.
|
||||
|
|
@ -1310,4 +1319,715 @@ enum SortOrder {
|
|||
|
||||
## Migrating from `t.crud()` to plain Nexus
|
||||
|
||||
Coming soon.
|
||||
`t.crud` is an experimental feature provided by the `nexus-plugin-prisma`. `t.crud` contains configurable GraphQL `Query` and `Mutation` resolvers
|
||||
that allow you to use create, read, update and delete operations against your Prisma models.
|
||||
|
||||
This section provides instructions how you can replace your `t.crud()` using the plain Nexus API.
|
||||
|
||||
The migration guide will be based on the following example Prisma schema:
|
||||
|
||||
```prisma
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
content String?
|
||||
authorId Int?
|
||||
published Boolean @default(false)
|
||||
author User? @relation(fields: [authorId], references: [id])
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
name String?
|
||||
email String @unique
|
||||
posts Post[]
|
||||
}
|
||||
```
|
||||
|
||||
### Resolvers
|
||||
|
||||
Queries and mutations require the resolve property to generate their appropriate responses.
|
||||
|
||||
This section gives an example how you can write your own resolvers for the different GraphQL operations.
|
||||
|
||||
#### Query
|
||||
|
||||
Here is an example query resolver:
|
||||
|
||||
```ts
|
||||
const Query = objectType({
|
||||
name: 'Query',
|
||||
definition(t) {
|
||||
t.field('posts', {
|
||||
type: 'Post',
|
||||
args: { /** ... query arguments */ },
|
||||
resolve: async (parent, args, ctx) => {
|
||||
const posts = ctx.prisma.post.findMany({
|
||||
where: { /** ...query PostWhereInput */ }
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Mutation
|
||||
|
||||
Here is an example of a mutation resolver:
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
t.field('createOnePost', {
|
||||
type: Post,
|
||||
args: { /** ...mutation arguments */ },
|
||||
resolve: async (parent, args, context) => {
|
||||
const post = await context.prisma.post.create({
|
||||
data: { /** ...mutation UserCreateInput */ }
|
||||
})
|
||||
return post
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Queries
|
||||
|
||||
#### `posts`
|
||||
|
||||
Assuming you have exposed the following resolver from your Prisma schema:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Query = queryType({
|
||||
definition(t) {
|
||||
t.crud.posts()
|
||||
}
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Post {
|
||||
author: User
|
||||
content: String
|
||||
id: Int!
|
||||
published: Boolean!
|
||||
title: String!
|
||||
}
|
||||
|
||||
type User {
|
||||
email: String!
|
||||
id: Int!
|
||||
name: String
|
||||
posts(after: PostWhereUniqueInput, before: PostWhereUniqueInput, first: Int, last: Int): [Post!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
posts(after: PostWhereUniqueInput, before: PostWhereUniqueInput, first: Int, last: Int): [Post!]!
|
||||
}
|
||||
|
||||
input PostWhereUniqueInput {
|
||||
id: Int
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
The above `Query` can be migrated to vanilla Nexus by making the following changes:
|
||||
|
||||
```ts
|
||||
const Query = objectType({
|
||||
name: "Query",
|
||||
definition(t) {
|
||||
| t.nonNull.list.nonNull.field("posts", {
|
||||
| type: Post,
|
||||
| args: {
|
||||
| after: arg({ type: PostWhereUniqueInput }),
|
||||
| before: arg({ type: PostWhereUniqueInput }),
|
||||
| first: intArg(),
|
||||
| last: intArg(),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
|
||||
const PostWhereUniqueInput = inputObjectType({
|
||||
name: "PostWhereUniqueInput",
|
||||
definition(t) {
|
||||
t.int("id")
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
#### `post`
|
||||
|
||||
Assume you have the `post` query available in your API:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Query = queryType({
|
||||
definition(t) {
|
||||
t.crud.post()
|
||||
}
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
post(where: PostWhereUniqueInput!): Post
|
||||
}
|
||||
|
||||
input PostWhereUniqueInput {
|
||||
id: Int
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
The fields can be migrated to plain Nexus by making the following changes:
|
||||
|
||||
```ts
|
||||
const Query = objectType({
|
||||
name: "Query",
|
||||
definition(t) {
|
||||
| t.field("post", {
|
||||
| type: Post,
|
||||
| args: {
|
||||
| where: arg({ type: nonNull(PostWhereUniqueInput) }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
|
||||
### Mutations
|
||||
|
||||
#### `createOnePost`
|
||||
|
||||
Assume the `createOnePost` mutation is made available in your API as follows:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.crud.createOnePost()
|
||||
}
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Mutation {
|
||||
createOnePost(data: PostCreateInput!): Post!
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
`createOnePost` is dependent on a number of `inputTypes` that are referenced in the existing API as follows:
|
||||
<details><summary>Expand to view all generated types</summary>
|
||||
|
||||
```graphql
|
||||
input PostCreateInput {
|
||||
author: UserCreateNestedOneWithoutPostsInput
|
||||
content: String
|
||||
published: Boolean
|
||||
title: String!
|
||||
}
|
||||
|
||||
input UserCreateNestedOneWithoutPostsInput {
|
||||
connect: UserWhereUniqueInput
|
||||
connectOrCreate: UserCreateOrConnectWithoutPostsInput
|
||||
create: UserCreateWithoutPostsInput
|
||||
}
|
||||
|
||||
input UserWhereUniqueInput {
|
||||
email: String
|
||||
id: Int
|
||||
}
|
||||
|
||||
input UserCreateOrConnectWithoutPostsInput {
|
||||
create: UserCreateWithoutPostsInput!
|
||||
where: UserWhereUniqueInput!
|
||||
}
|
||||
|
||||
input UserCreateWithoutPostsInput {
|
||||
email: String!
|
||||
name: String
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
The `Query` can be migrated to plain Nexus as follows:
|
||||
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
| t.nonNull.field("createOnePost", {
|
||||
| type: Post,
|
||||
| args: {
|
||||
| data: arg({ type: nonNull(PostCreateInput) }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
|
||||
const PostCreateInput = inputObjectType({
|
||||
name: "PostCreateInput",
|
||||
definition(t) {
|
||||
t.field("author", { type: UserCreateNestedOneWithoutPostsInput })
|
||||
t.string("content")
|
||||
t.boolean("published")
|
||||
t.nonNull.string("title")
|
||||
}
|
||||
});
|
||||
|
||||
const UserCreateNestedOneWithoutPostsInput = inputObjectType({
|
||||
name: "UserCreateNestedOneWithoutPostsInput",
|
||||
definition(t) {
|
||||
t.field("connect", { type: UserWhereUniqueInput })
|
||||
t.field("connectOrCreate", { type: UserCreateOrConnectWithoutPostsInput })
|
||||
t.field("create", { type: UserCreateWithoutPostsInput })
|
||||
}
|
||||
});
|
||||
|
||||
const UserWhereUniqueInput = inputObjectType({
|
||||
name: "UserWhereUniqueInput",
|
||||
definition(t) {
|
||||
t.string("email")
|
||||
t.int("id")
|
||||
}
|
||||
});
|
||||
|
||||
const UserCreateOrConnectWithoutPostsInput = inputObjectType({
|
||||
name: "UserCreateOrConnectWithoutPostsInput",
|
||||
definition(t) {
|
||||
t.nonNull.field("create", { type: UserCreateWithoutPostsInput })
|
||||
t.nonNull.field("where", { type: UserWhereUniqueInput })
|
||||
}
|
||||
});
|
||||
|
||||
const UserCreateWithoutPostsInput = inputObjectType({
|
||||
name: "UserCreateWithoutPostsInput",
|
||||
definition(t) {
|
||||
t.nonNull.string("email")
|
||||
t.string("name")
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
#### `updateOnePost`
|
||||
|
||||
Assume your `updateOnePost` mutation is made available in your API:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.crud.updateOnePost()
|
||||
}
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
updateOnePost(data: PostUpdateInput!, where: PostWhereUniqueInput!): Post
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
The referenced input types mentioned in `updateOnePost` mutation are as follows:
|
||||
|
||||
<details><summary>Expand to view all generated types</summary>
|
||||
|
||||
```graphql
|
||||
input PostUpdateInput {
|
||||
author: UserUpdateOneWithoutPostsInput
|
||||
content: NullableStringFieldUpdateOperationsInput
|
||||
published: BoolFieldUpdateOperationsInput
|
||||
title: StringFieldUpdateOperationsInput
|
||||
}
|
||||
|
||||
input PostWhereUniqueInput {
|
||||
id: Int
|
||||
}
|
||||
|
||||
input UserUpdateOneWithoutPostsInput {
|
||||
connect: UserWhereUniqueInput
|
||||
connectOrCreate: UserCreateOrConnectWithoutPostsInput
|
||||
create: UserCreateWithoutPostsInput
|
||||
delete: Boolean
|
||||
disconnect: Boolean
|
||||
update: UserUpdateWithoutPostsInput
|
||||
upsert: UserUpsertWithoutPostsInput
|
||||
}
|
||||
|
||||
input NullableStringFieldUpdateOperationsInput {
|
||||
set: String
|
||||
}
|
||||
|
||||
input BoolFieldUpdateOperationsInput {
|
||||
set: Boolean
|
||||
}
|
||||
|
||||
input StringFieldUpdateOperationsInput {
|
||||
set: String
|
||||
}
|
||||
|
||||
input UserUpdateWithoutPostsInput {
|
||||
email: StringFieldUpdateOperationsInput
|
||||
name: NullableStringFieldUpdateOperationsInput
|
||||
}
|
||||
|
||||
input UserUpsertWithoutPostsInput {
|
||||
create: UserCreateWithoutPostsInput!
|
||||
update: UserUpdateWithoutPostsInput!
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
The above can be migrated to vanilla Nexus as follows:
|
||||
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
| t.field("updateOnePost", {
|
||||
| type: Post,
|
||||
| args: {
|
||||
| data: arg({ type: nonNull(PostUpdateInput) }),
|
||||
| where: arg({ type: nonNull(PostWhereUniqueInput) }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
|
||||
const PostUpdateInput = inputObjectType({
|
||||
name: "PostUpdateInput",
|
||||
definition(t) {
|
||||
t.field("author", { type: UserUpdateOneWithoutPostsInput })
|
||||
t.field("content", { type: NullableStringFieldUpdateOperationsInput })
|
||||
t.field("published", { type: BoolFieldUpdateOperationsInput })
|
||||
t.field("title", { type: StringFieldUpdateOperationsInput })
|
||||
}
|
||||
});
|
||||
|
||||
const PostWhereUniqueInput = inputObjectType({
|
||||
name: "PostWhereUniqueInput",
|
||||
definition(t) {
|
||||
t.int("id")
|
||||
}
|
||||
});
|
||||
|
||||
const UserUpdateOneWithoutPostsInput = inputObjectType({
|
||||
name: "UserUpdateOneWithoutPostsInput",
|
||||
definition(t) {
|
||||
t.field("connect", { type: UserWhereUniqueInput })
|
||||
t.field("connectOrCreate", { type: UserCreateOrConnectWithoutPostsInput })
|
||||
t.field("create", { type: UserCreateWithoutPostsInput })
|
||||
t.boolean("delete")
|
||||
t.boolean("disconnect")
|
||||
t.field("update", { type: UserUpdateWithoutPostsInput })
|
||||
t.field("upsert", { type: UserUpsertWithoutPostsInput })
|
||||
}
|
||||
});
|
||||
|
||||
const NullableStringFieldUpdateOperationsInput = inputObjectType({
|
||||
name: "NullableStringFieldUpdateOperationsInput",
|
||||
definition(t) {
|
||||
t.string("set")
|
||||
}
|
||||
});
|
||||
|
||||
const BoolFieldUpdateOperationsInput = inputObjectType({
|
||||
name: "BoolFieldUpdateOperationsInput",
|
||||
definition(t) {
|
||||
t.boolean("set")
|
||||
}
|
||||
});
|
||||
|
||||
const StringFieldUpdateOperationsInput = inputObjectType({
|
||||
name: "StringFieldUpdateOperationsInput",
|
||||
definition(t) {
|
||||
t.string("set")
|
||||
}
|
||||
});
|
||||
|
||||
const UserUpdateWithoutPostsInput = inputObjectType({
|
||||
name: "UserUpdateWithoutPostsInput",
|
||||
definition(t) {
|
||||
t.field("email", { type: StringFieldUpdateOperationsInput })
|
||||
t.field("name", { type: NullableStringFieldUpdateOperationsInput })
|
||||
}
|
||||
});
|
||||
|
||||
const UserUpsertWithoutPostsInput = inputObjectType({
|
||||
name: "UserUpsertWithoutPostsInput",
|
||||
definition(t) {
|
||||
t.nonNull.field("create", { type: UserCreateWithoutPostsInput })
|
||||
t.nonNull.field("update", { type: UserUpdateWithoutPostsInput })
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
#### `updateManyPost`
|
||||
|
||||
Assume you have made your `updateManyPost` mutation available in your API:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.crud.updateManyPost()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
updateManyPost(data: PostUpdateManyMutationInput!, where: PostWhereInput): AffectedRowsOutput!
|
||||
}
|
||||
|
||||
type AffectedRowsOutput {
|
||||
count: Int!
|
||||
}
|
||||
|
||||
input PostUpdateManyMutationInput {
|
||||
content: NullableStringFieldUpdateOperationsInput
|
||||
published: BoolFieldUpdateOperationsInput
|
||||
title: StringFieldUpdateOperationsInput
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
You can migrate the `Mutation` to plain Nexus as follows:
|
||||
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
| t.nonNull.field("updateManyPost", {
|
||||
| type: AffectedRowsOutput,
|
||||
| args: {
|
||||
| data: arg({ type: nonNull(PostUpdateManyMutationInput) }),
|
||||
| where: arg({ type: PostWhereInput }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
|
||||
const AffectedRowsOutput = objectType({
|
||||
name: "AffectedRowsOutput",
|
||||
definition(t) {
|
||||
t.nonNull.int("count")
|
||||
}
|
||||
})
|
||||
|
||||
const PostUpdateManyMutationInput = inputObjectType({
|
||||
name: "PostUpdateManyMutationInput",
|
||||
definition(t) {
|
||||
t.field("content", { type: NullableStringFieldUpdateOperationsInput })
|
||||
t.field("published", { type: BoolFieldUpdateOperationsInput })
|
||||
t.field("title", { type: StringFieldUpdateOperationsInput })
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
#### `upsertOnePost`
|
||||
|
||||
Assume the `upsertOnePost` GraphQL operation is available in your API:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.crud.upsertOnePost()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
upsertOnePost(create: PostCreateInput!, update: PostUpdateInput!, where: PostWhereUniqueInput!): Post!
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
The above field can be migrated to vanilla Nexus by making the following change:
|
||||
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
| t.nonNull.field("upsertOnePost", {
|
||||
| type: Post,
|
||||
| args: {
|
||||
| create: arg({ type: nonNull(PostCreateInput) }),
|
||||
| update: arg({ type: nonNull(PostUpdateInput) }),
|
||||
| where: arg({ type: nonNull(PostWhereUniqueInput) }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
#### `deleteOnePost`
|
||||
|
||||
Assume the `deleteOnePost` mutation is made available in your API:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.crud.deleteOnePost()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
deleteOnePost(where: PostWhereUniqueInput!): Post
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
The above `Mutation` can be migrated to plain Nexus by making the following changes:
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
| t.field("deleteOnePost", {
|
||||
| type: Post,
|
||||
| args: {
|
||||
| where: arg({ type: nonNull(PostWhereUniqueInput) }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
||||
#### `deleteManyPost`
|
||||
|
||||
Assume you have the `deleteManyPost` available in your API:
|
||||
|
||||
<TabbedContent tabs={[<FileWithIcon text="Nexus types" icon="code"/>, <FileWithIcon text="GraphQL types" icon="code"/>]}>
|
||||
|
||||
<tab>
|
||||
|
||||
```ts
|
||||
const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.crud.deleteManyPost()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
<tab>
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
deleteManyPost(where: PostWhereInput!): AffectedRowsOutput
|
||||
}
|
||||
```
|
||||
|
||||
</tab>
|
||||
|
||||
</TabbedContent>
|
||||
|
||||
You can migrate it to plain Nexus by making the following changes:
|
||||
```ts
|
||||
const Mutation = objectType({
|
||||
name: "Mutation",
|
||||
definition(t) {
|
||||
| t.nonNull.field("deleteManyPost", {
|
||||
| type: AffectedRowsOutput,
|
||||
| args: {
|
||||
| where: arg({ type: PostWhereInput }),
|
||||
| },
|
||||
| })
|
||||
})
|
||||
```
|
||||
|
||||
> **Note**: Convert your existing SDL to Nexus code with the [SDL Converter](https://nexusjs.org/converter). Refer to the [resolvers](#resolvers) section on how to add the resolve property to your GraphQL operation.
|
||||
|
|
@ -27,7 +27,7 @@ You will need to manage the TypeScript toolchain yourself.
|
|||
1. Install `typescript`, `ts-node`, and `ts-node-dev`.
|
||||
|
||||
```bash-symbol
|
||||
npm add -D typescript ts-node ts-node-dev
|
||||
npm install -D typescript ts-node ts-node-dev
|
||||
```
|
||||
|
||||
2. The following applies to VSCode but something like it might apply to other editors as well. You must also make sure your editor is set to use the local version of TypeScript rather than the one the editor ships with. Summon the command pallet `command+shift+p` and then enter and select `typescript: select TypeScript Version...`. Then select `Workspace Version`.
|
||||
|
|
@ -205,13 +205,13 @@ Nexus framework comes bundled with Apollo Server and runs it for you. You will n
|
|||
1. Install new dependencies
|
||||
|
||||
```tsx
|
||||
npm add express apollo-server-express
|
||||
npm install express apollo-server-express
|
||||
```
|
||||
|
||||
2. If using TypeScript then Install typings for Express
|
||||
|
||||
```tsx
|
||||
npm add -D @types/express
|
||||
npm install -D @types/express
|
||||
```
|
||||
|
||||
3. If using TypeScript then you [need to](https://github.com/apollographql/apollo-server/issues/1977#issuecomment-662946590) enable `esModuleInterop` to be able to use Apollo Server
|
||||
|
|
@ -337,7 +337,7 @@ Nexus Framework has some builtin logging functionality. You can approximate it a
|
|||
1. Install your logger
|
||||
|
||||
```tsx
|
||||
npm add floggy
|
||||
npm install floggy
|
||||
```
|
||||
|
||||
2. Create your application logger. The logger you export here should be used across your codebase.
|
||||
|
|
@ -386,13 +386,13 @@ You need to explicitly setup all custom scalars yourself. To match what Nexus Fr
|
|||
1. Install the `graphql-scalars` package
|
||||
|
||||
```json
|
||||
npm add graphql-scalars
|
||||
npm install graphql-scalars
|
||||
```
|
||||
|
||||
2. Setup the `Json` and `DateTime` scalars
|
||||
|
||||
```tsx
|
||||
import { makeSchema } from 'nexus'
|
||||
import { makeSchema, asNexusMethod } from 'nexus'
|
||||
import { DateTimeResolver, JSONObjectResolver } from 'graphql-scalars'
|
||||
|
||||
const jsonScalar = asNexusMethod(JSONObjectResolver, 'json')
|
||||
|
|
@ -448,7 +448,7 @@ import * as Path from 'path'
|
|||
|
||||
Nexus.makeSchema({
|
||||
contextType: {
|
||||
module: Path.join(__dirname, './path/to/contextModule'),
|
||||
module: Path.join(__dirname, './path/to/contextModule.ts'),
|
||||
alias: 'ContextModule',
|
||||
export: 'Context'
|
||||
},
|
||||
|
|
@ -536,13 +536,13 @@ In Nexus Framework there was a testing module for system testing your app. You n
|
|||
1. Install your test framework
|
||||
|
||||
```tsx
|
||||
npm add jest
|
||||
npm install jest
|
||||
```
|
||||
|
||||
2. If you are a TypeScript user then install and configure `ts-jest`
|
||||
|
||||
```bash
|
||||
npm add ts-jest
|
||||
npm install ts-jest
|
||||
```
|
||||
|
||||
We will configure using a `jest.config.js` module in your project root. Do not use a JSON based configuration unless you know that you won't need the dynamic code used in some later steps here.
|
||||
|
|
@ -639,7 +639,7 @@ In Nexus Framework there was a testing module for system testing your app. You n
|
|||
6. You will need a way to run your app in tests. Here is one way [adapted from the tutorial](https://todo.com).
|
||||
|
||||
```tsx
|
||||
npm add -D graphql-request
|
||||
npm install -D graphql-request
|
||||
```
|
||||
|
||||
```tsx
|
||||
|
|
@ -688,7 +688,7 @@ Nexus Framework had a CLI for scaffolding new projects. You can approximate this
|
|||
Nexus Framework has a gradual settings API with features such as automatic mapping of environment variables to settings. You can use the [`setset`](https://github.com/jasonkuhrt/setset) package manually to gain back this functionality.
|
||||
|
||||
```tsx
|
||||
npm add setset
|
||||
npm install setset
|
||||
```
|
||||
|
||||
```tsx
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ If you're looking to migrate from the Nexus Framework over to Nexus and you're u
|
|||
You will need to install the Prisma dependencies yourself now.
|
||||
|
||||
```
|
||||
npm add -D @prisma/cli
|
||||
npm add @prisma/client
|
||||
npm install -D @prisma/cli
|
||||
npm install @prisma/client
|
||||
```
|
||||
|
||||
You must also make sure that you are using the version `0.20` or later of `nexus-plugin-prisma`.
|
||||
|
|
@ -82,7 +82,7 @@ export const schema = makeSchema({
|
|||
|
||||
### Configuring Context Type
|
||||
|
||||
The framework used to automatically inject and type your context so that an instance of the client would be there for you. We know need to configure that manually. To do so, we'll first create a `context.ts` file where we'll export a type containing the Prisma Client.
|
||||
The framework used to automatically inject and type your context so that an instance of the client would be there for you. We now need to configure that manually. To do so, we'll first create a `context.ts` file where we'll export a type containing the Prisma Client.
|
||||
|
||||
```tsx
|
||||
// context.ts
|
||||
|
|
@ -128,7 +128,7 @@ If your Prisma Schema is using either the `Json` or `DateTime` type, the framewo
|
|||
|
||||
```bash
|
||||
npm install graphql-scalars
|
||||
```
|
||||
```xf
|
||||
|
||||
2. Then, add the following configuration property to the Prisma plugin
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ Check out the [example projects](https://github.com/graphql-nexus/nexus/tree/mai
|
|||
## Installation
|
||||
|
||||
```sh
|
||||
npm add nexus
|
||||
npm add graphql # required as a peer dependency
|
||||
npm install nexus
|
||||
npm install graphql # required as a peer dependency
|
||||
```
|
||||
|
||||
If you are using TypeScript version `4.1` is suggested. Nexus doesn't have a hard requirement for it yet but may in a future non-breaking release.
|
||||
|
|
|
|||
|
|
@ -214,8 +214,7 @@ const Footer = ({ footerProps }: FooterViewProps) => {
|
|||
}
|
||||
</div>
|
||||
|
||||
<p className="social-text">Prisma © 2018-2020</p>
|
||||
<p>Made with ❤️ in Berlin</p>
|
||||
<p className="social-text">Tim Griesser © 2018-2022</p>
|
||||
</div>
|
||||
</SocialWrapper>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -249,7 +249,6 @@ hr {
|
|||
|
||||
.list li {
|
||||
padding-left: 20px;
|
||||
text-indent: -1em;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,6 @@ npm run examples
|
|||
- [apollo-fullstack](apollo-fullstack)
|
||||
- [star-wars](star-wars)
|
||||
- [kitchen-sink](kitchen-sink)
|
||||
- [nexus-prisma](nexus-prisma)
|
||||
- [with-prisma](with-prisma)
|
||||
|
||||
> The `nexus-prisma` example is not included when all examples are ran at once. You can try it by following the instructions in its [README](nexus-prisma) instead.
|
||||
> The `with-prisma` example is not included when all examples are ran at once. You can try it by following the instructions in its [README](with-prisma) instead.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"apollo-server-testing": "^2.18.1",
|
||||
"dedent": "^0.7.0",
|
||||
"fullstack-tutorial": "apollographql/fullstack-tutorial.git",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-tools": "^4.0.7",
|
||||
"isemail": "^3.2.0",
|
||||
"nexus": "^1.0.0",
|
||||
|
|
@ -39,6 +39,6 @@
|
|||
"node-fetch": "^2.2.1",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-node-dev": "^1.0.0-pre.30",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { idArg, list, nonNull, nullable, objectType, stringArg } from 'nexus'
|
||||
import { idArg, list, nonNull, objectType, stringArg } from 'nexus'
|
||||
|
||||
export const Mutation = objectType({
|
||||
name: 'Mutation',
|
||||
|
|
|
|||
|
|
@ -3169,6 +3169,11 @@ graphql@^15.3.0:
|
|||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
growly@^1.2.0, growly@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||
|
|
@ -6799,10 +6804,10 @@ type-is@~1.6.17, type-is@~1.6.18:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
typescript@^3.9:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.4.9"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"dataloader": "tgriesser/dataloader.git#ts-types",
|
||||
"express": "^4.16.4",
|
||||
"ghost": "^3.35.3",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-scalars": "^1.2.6",
|
||||
"graphql-tools": "^4.0.7",
|
||||
"knex": "^0.19.5",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"prettier": "^1.19.1",
|
||||
"ts-node": "^8.0.2",
|
||||
"ts-node-dev": "^1.0.0-pre.30",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,7 +6,7 @@
|
|||
"dependencies": {
|
||||
"apollo-server": "^2.18.1",
|
||||
"githunt-api": "peggyrayzis/GitHunt-API",
|
||||
"graphql": "^14.5.8",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-tools": "^4.0.7",
|
||||
"nexus": "^1.0.0"
|
||||
},
|
||||
|
|
@ -14,6 +14,6 @@
|
|||
"nodemon": "^1.18.6",
|
||||
"prettier": "^1.19.1",
|
||||
"sqlite3": "^4.1.0",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2962,18 +2962,16 @@ graphql-upload@^8.0.2:
|
|||
dependencies:
|
||||
iterall "^1.1.0"
|
||||
|
||||
graphql@^14.5.8:
|
||||
version "14.5.8"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.5.8.tgz#504f3d3114cb9a0a3f359bbbcf38d9e5bf6a6b3c"
|
||||
integrity sha512-MMwmi0zlVLQKLdGiMfWkgQD7dY/TUKt4L+zgJ/aR0Howebod3aNgP5JkgvAULiR2HPVZaP2VEElqtdidHweLkg==
|
||||
dependencies:
|
||||
iterall "^1.2.2"
|
||||
|
||||
graphql@^15.3.0:
|
||||
version "15.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
har-schema@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||
|
|
@ -3516,7 +3514,7 @@ isstream@~0.1.2:
|
|||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
iterall@^1.1.0, iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2:
|
||||
iterall@^1.1.0, iterall@^1.1.3, iterall@^1.2.1:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7"
|
||||
integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==
|
||||
|
|
@ -5588,10 +5586,10 @@ type-is@~1.6.14, type-is@~1.6.17, type-is@~1.6.18:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
typescript@^3.9:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
uid-safe@~2.1.4:
|
||||
version "2.1.5"
|
||||
|
|
|
|||
|
|
@ -19,24 +19,24 @@ interface Baz {
|
|||
|
||||
type BooleanConnection {
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Edge-Types
|
||||
https://relay.dev/graphql/connections.htm#sec-Edge-Types
|
||||
"""
|
||||
edges: [BooleanEdge]
|
||||
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo
|
||||
https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type BooleanEdge {
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Cursor
|
||||
https://relay.dev/graphql/connections.htm#sec-Cursor
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Node
|
||||
https://relay.dev/graphql/connections.htm#sec-Node
|
||||
"""
|
||||
node: Boolean
|
||||
}
|
||||
|
|
@ -49,24 +49,24 @@ scalar Date
|
|||
|
||||
type DateConnection {
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Edge-Types
|
||||
https://relay.dev/graphql/connections.htm#sec-Edge-Types
|
||||
"""
|
||||
edges: [DateEdge]
|
||||
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo
|
||||
https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type DateEdge {
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Cursor
|
||||
https://relay.dev/graphql/connections.htm#sec-Cursor
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Node
|
||||
https://relay.dev/graphql/connections.htm#sec-Node
|
||||
"""
|
||||
node: Date
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ interface Node {
|
|||
}
|
||||
|
||||
"""
|
||||
PageInfo cursor, as defined in https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo
|
||||
PageInfo cursor, as defined in https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo
|
||||
"""
|
||||
type PageInfo {
|
||||
"""
|
||||
|
|
@ -306,24 +306,24 @@ type User {
|
|||
|
||||
type UserConnection {
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Edge-Types
|
||||
https://relay.dev/graphql/connections.htm#sec-Edge-Types
|
||||
"""
|
||||
edges: [UserEdge]
|
||||
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo
|
||||
https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type UserEdge {
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Cursor
|
||||
https://relay.dev/graphql/connections.htm#sec-Cursor
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
https://facebook.github.io/relay/graphql/connections.htm#sec-Node
|
||||
https://relay.dev/graphql/connections.htm#sec-Node
|
||||
"""
|
||||
node: User
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"apollo-server": "^2.18.1",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-query-complexity": "^0.4.1",
|
||||
"graphql-relay": "^0.6.0",
|
||||
"graphql-subscriptions": "^1.0.0",
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
"nexus": "^1.0.0",
|
||||
"subscriptions-transport-ws": "^0.9.15",
|
||||
"ts-node": "^8.5.4",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/graphql-relay": "^0.4.11",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ const schema = makeSchema({
|
|||
types,
|
||||
outputs: {
|
||||
schema: path.join(__dirname, '../kitchen-sink-schema.graphql'),
|
||||
typegen: path.join(__dirname, './kitchen-sink.gen.ts'),
|
||||
typegen: {
|
||||
outputPath: path.join(__dirname, './kitchen-sink.gen.ts'),
|
||||
globalsPath: path.join(__dirname, './kitchen-sink-globals.gen.ts'),
|
||||
declareInputs: true,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
NodePlugin,
|
||||
|
|
|
|||
|
|
@ -391,9 +391,17 @@ export const MoreQueryFields = extendType({
|
|||
|
||||
export const DateScalar = scalarType({
|
||||
name: 'Date',
|
||||
serialize: (value) => value.getTime(),
|
||||
parseValue: (value) => new Date(value),
|
||||
parseLiteral: (ast) => (ast.kind === 'IntValue' ? new Date(ast.value) : null),
|
||||
serialize: (value) => (value as Date).toISOString(),
|
||||
parseValue: (value) => new Date(value as string | number),
|
||||
parseLiteral: (ast) => {
|
||||
if (ast.kind === 'IntValue' || ast.kind === 'StringValue') {
|
||||
const d = new Date(ast.value)
|
||||
if (!isNaN(d.valueOf())) {
|
||||
return d
|
||||
}
|
||||
}
|
||||
throw new Error('Invalid date')
|
||||
},
|
||||
asNexusMethod: 'date',
|
||||
sourceType: 'Date',
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/** This file was generated by Nexus Schema Do not make changes to this file directly */
|
||||
|
||||
import type { NexusGenTypes } from './kitchen-sink.gen'
|
||||
declare global {
|
||||
interface NexusGenCustomInputMethods<TypeName extends string> {
|
||||
date<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
opts?: core.CommonInputFieldConfig<TypeName, FieldName>
|
||||
): void // "Date";
|
||||
}
|
||||
}
|
||||
declare global {
|
||||
interface NexusGenCustomOutputMethods<TypeName extends string> {
|
||||
date<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
...opts: core.ScalarOutSpread<TypeName, FieldName>
|
||||
): void // "Date";
|
||||
/**
|
||||
* Adds a Relay-style connection to the type, with numerous options for configuration
|
||||
*
|
||||
* @see https://nexusjs.org/docs/plugins/connection
|
||||
*/
|
||||
connectionField<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
config: connectionPluginCore.ConnectionFieldConfig<TypeName, FieldName>
|
||||
): void
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface NexusGen extends NexusGenTypes {}
|
||||
}
|
||||
|
||||
import type { core, connectionPluginCore } from 'nexus'
|
||||
import type { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'
|
||||
import type { FieldAuthorizeResolver } from 'nexus/dist/plugins/fieldAuthorizePlugin'
|
||||
|
||||
declare global {
|
||||
interface NexusGenPluginTypeConfig<TypeName extends string> {
|
||||
node?: string | core.FieldResolver<TypeName, any>
|
||||
}
|
||||
interface NexusGenPluginInputTypeConfig<TypeName extends string> {}
|
||||
interface NexusGenPluginFieldConfig<TypeName extends string, FieldName extends string> {
|
||||
/**
|
||||
* The complexity for an individual field. Return a number or a function that returns a number to specify
|
||||
* the complexity for this field.
|
||||
*/
|
||||
complexity?: QueryComplexity<TypeName, FieldName>
|
||||
/**
|
||||
* Authorization for an individual field. Returning "true" or "Promise<true>" means the field can be
|
||||
* accessed. Returning "false" or "Promise<false>" will respond with a "Not Authorized" error for the
|
||||
* field. Returning or throwing an error will also prevent the resolver from executing.
|
||||
*/
|
||||
authorize?: FieldAuthorizeResolver<TypeName, FieldName>
|
||||
/**
|
||||
* The nullability guard can be helpful, but is also a potentially expensive operation for lists. We need
|
||||
* to iterate the entire list to check for null items to guard against. Set this to true to skip the null
|
||||
* guard on a specific field if you know there's no potential for unsafe types.
|
||||
*/
|
||||
skipNullGuard?: boolean
|
||||
}
|
||||
interface NexusGenPluginInputFieldConfig<TypeName extends string, FieldName extends string> {}
|
||||
interface NexusGenPluginSchemaConfig {}
|
||||
interface NexusGenPluginArgConfig {}
|
||||
}
|
||||
|
|
@ -1,61 +1,34 @@
|
|||
/** This file was generated by Nexus Schema Do not make changes to this file directly */
|
||||
|
||||
import { UnusedInterfaceTypeDef } from './kitchen-sink-definitions'
|
||||
import { core, connectionPluginCore } from 'nexus'
|
||||
import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'
|
||||
import { FieldAuthorizeResolver } from 'nexus/dist/plugins/fieldAuthorizePlugin'
|
||||
declare global {
|
||||
interface NexusGenCustomInputMethods<TypeName extends string> {
|
||||
date<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
opts?: core.CommonInputFieldConfig<TypeName, FieldName>
|
||||
): void // "Date";
|
||||
}
|
||||
}
|
||||
declare global {
|
||||
interface NexusGenCustomOutputMethods<TypeName extends string> {
|
||||
date<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
...opts: core.ScalarOutSpread<TypeName, FieldName>
|
||||
): void // "Date";
|
||||
/**
|
||||
* Adds a Relay-style connection to the type, with numerous options for configuration
|
||||
*
|
||||
* @see https://nexusjs.org/docs/plugins/connection
|
||||
*/
|
||||
connectionField<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
config: connectionPluginCore.ConnectionFieldConfig<TypeName, FieldName>
|
||||
): void
|
||||
}
|
||||
import type { UnusedInterfaceTypeDef } from './kitchen-sink-definitions'
|
||||
import type { core } from 'nexus'
|
||||
|
||||
export interface InputType {
|
||||
answer?: number | null // Int
|
||||
key: string // String!
|
||||
nestedInput?: InputType2 | null // InputType2
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface NexusGen extends NexusGenTypes {}
|
||||
export interface InputType2 {
|
||||
answer?: number | null // Int
|
||||
key: string // String!
|
||||
someDate: NexusGenScalars['Date'] // Date!
|
||||
}
|
||||
|
||||
export interface NestedType {
|
||||
veryNested?: string | null // String
|
||||
}
|
||||
|
||||
export interface SomeArg {
|
||||
arg?: NestedType | null // NestedType
|
||||
someField?: string | null // String
|
||||
}
|
||||
|
||||
export interface NexusGenInputs {
|
||||
InputType: {
|
||||
// input type
|
||||
answer?: number | null // Int
|
||||
key: string // String!
|
||||
nestedInput?: NexusGenInputs['InputType2'] | null // InputType2
|
||||
}
|
||||
InputType2: {
|
||||
// input type
|
||||
answer?: number | null // Int
|
||||
key: string // String!
|
||||
someDate: NexusGenScalars['Date'] // Date!
|
||||
}
|
||||
NestedType: {
|
||||
// input type
|
||||
veryNested?: string | null // String
|
||||
}
|
||||
SomeArg: {
|
||||
// input type
|
||||
arg?: NexusGenInputs['NestedType'] | null // NestedType
|
||||
someField?: string | null // String
|
||||
}
|
||||
InputType: InputType
|
||||
InputType2: InputType2
|
||||
NestedType: NestedType
|
||||
SomeArg: SomeArg
|
||||
}
|
||||
|
||||
export interface NexusGenEnums {}
|
||||
|
|
@ -390,100 +363,117 @@ export interface NexusGenFieldTypeNames {
|
|||
}
|
||||
}
|
||||
|
||||
export interface FooArgsTestArgs {
|
||||
a: InputType | null // InputType
|
||||
}
|
||||
|
||||
export interface MutationSomeMutationFieldArgs {
|
||||
id: string // ID!
|
||||
}
|
||||
|
||||
export interface QueryAsArgExampleArgs {
|
||||
testAsArg: InputType // InputType!
|
||||
}
|
||||
|
||||
export interface QueryBooleanConnectionArgs {
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
}
|
||||
|
||||
export interface QueryComplexQueryArgs {
|
||||
count: number // Int!
|
||||
}
|
||||
|
||||
export interface QueryDeprecatedConnectionArgs {
|
||||
after?: string | null // String
|
||||
before?: string | null // String
|
||||
first?: number | null // Int
|
||||
last?: number | null // Int
|
||||
}
|
||||
|
||||
export interface QueryGetNumberOrNullArgs {
|
||||
a: number // Int!
|
||||
}
|
||||
|
||||
export interface QueryGuardedConnectionArgs {
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
}
|
||||
|
||||
export interface QueryInlineArgsArgs {
|
||||
someArg?: SomeArg | null // SomeArg
|
||||
}
|
||||
|
||||
export interface QueryInputAsArgExampleArgs {
|
||||
testInput?: InputType | null // InputType
|
||||
testScalar?: string | null // String
|
||||
}
|
||||
|
||||
export interface QueryUserConnectionAdditionalArgsArgs {
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
isEven?: boolean | null // Boolean
|
||||
}
|
||||
|
||||
export interface QueryUserConnectionBackwardOnlyArgs {
|
||||
before?: string | null // String
|
||||
last: number // Int!
|
||||
}
|
||||
|
||||
export interface QueryUserConnectionForwardOnlyArgs {
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
}
|
||||
|
||||
export interface QueryUsersConnectionNodesArgs {
|
||||
after?: string | null // String
|
||||
before?: string | null // String
|
||||
first?: number | null // Int
|
||||
last?: number | null // Int
|
||||
}
|
||||
|
||||
export interface QueryUsersConnectionResolveArgs {
|
||||
after?: string | null // String
|
||||
before?: string | null // String
|
||||
first?: number | null // Int
|
||||
last?: number | null // Int
|
||||
}
|
||||
|
||||
export interface TestObjArgsTestArgs {
|
||||
a: InputType | null // InputType
|
||||
}
|
||||
|
||||
export interface BarArgsTestArgs {
|
||||
a: InputType | null // InputType
|
||||
}
|
||||
|
||||
export interface NexusGenArgTypes {
|
||||
Foo: {
|
||||
argsTest: {
|
||||
// args
|
||||
a: NexusGenInputs['InputType'] | null // InputType
|
||||
}
|
||||
argsTest: FooArgsTestArgs
|
||||
}
|
||||
Mutation: {
|
||||
someMutationField: {
|
||||
// args
|
||||
id: string // ID!
|
||||
}
|
||||
someMutationField: MutationSomeMutationFieldArgs
|
||||
}
|
||||
Query: {
|
||||
asArgExample: {
|
||||
// args
|
||||
testAsArg: NexusGenInputs['InputType'] // InputType!
|
||||
}
|
||||
booleanConnection: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
}
|
||||
complexQuery: {
|
||||
// args
|
||||
count: number // Int!
|
||||
}
|
||||
deprecatedConnection: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
before?: string | null // String
|
||||
first?: number | null // Int
|
||||
last?: number | null // Int
|
||||
}
|
||||
getNumberOrNull: {
|
||||
// args
|
||||
a: number // Int!
|
||||
}
|
||||
guardedConnection: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
}
|
||||
inlineArgs: {
|
||||
// args
|
||||
someArg?: NexusGenInputs['SomeArg'] | null // SomeArg
|
||||
}
|
||||
inputAsArgExample: {
|
||||
// args
|
||||
testInput?: NexusGenInputs['InputType'] | null // InputType
|
||||
testScalar?: string | null // String
|
||||
}
|
||||
userConnectionAdditionalArgs: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
isEven?: boolean | null // Boolean
|
||||
}
|
||||
userConnectionBackwardOnly: {
|
||||
// args
|
||||
before?: string | null // String
|
||||
last: number // Int!
|
||||
}
|
||||
userConnectionForwardOnly: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
first: number // Int!
|
||||
}
|
||||
usersConnectionNodes: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
before?: string | null // String
|
||||
first?: number | null // Int
|
||||
last?: number | null // Int
|
||||
}
|
||||
usersConnectionResolve: {
|
||||
// args
|
||||
after?: string | null // String
|
||||
before?: string | null // String
|
||||
first?: number | null // Int
|
||||
last?: number | null // Int
|
||||
}
|
||||
asArgExample: QueryAsArgExampleArgs
|
||||
booleanConnection: QueryBooleanConnectionArgs
|
||||
complexQuery: QueryComplexQueryArgs
|
||||
deprecatedConnection: QueryDeprecatedConnectionArgs
|
||||
getNumberOrNull: QueryGetNumberOrNullArgs
|
||||
guardedConnection: QueryGuardedConnectionArgs
|
||||
inlineArgs: QueryInlineArgsArgs
|
||||
inputAsArgExample: QueryInputAsArgExampleArgs
|
||||
userConnectionAdditionalArgs: QueryUserConnectionAdditionalArgsArgs
|
||||
userConnectionBackwardOnly: QueryUserConnectionBackwardOnlyArgs
|
||||
userConnectionForwardOnly: QueryUserConnectionForwardOnlyArgs
|
||||
usersConnectionNodes: QueryUsersConnectionNodesArgs
|
||||
usersConnectionResolve: QueryUsersConnectionResolveArgs
|
||||
}
|
||||
TestObj: {
|
||||
argsTest: {
|
||||
// args
|
||||
a: NexusGenInputs['InputType'] | null // InputType
|
||||
}
|
||||
argsTest: TestObjArgsTestArgs
|
||||
}
|
||||
Bar: {
|
||||
argsTest: {
|
||||
// args
|
||||
a: NexusGenInputs['InputType'] | null // InputType
|
||||
}
|
||||
argsTest: BarArgsTestArgs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -554,99 +544,3 @@ export interface NexusGenTypes {
|
|||
abstractsUsingStrategyResolveType: NexusGenAbstractsUsingStrategyResolveType
|
||||
features: NexusGenFeaturesConfig
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface NexusGenPluginTypeConfig<TypeName extends string> {
|
||||
node?: string | core.FieldResolver<TypeName, any>
|
||||
}
|
||||
interface NexusGenPluginFieldConfig<TypeName extends string, FieldName extends string> {
|
||||
/**
|
||||
* The complexity for an individual field. Return a number or a function that returns a number to specify
|
||||
* the complexity for this field.
|
||||
*/
|
||||
complexity?: QueryComplexity<TypeName, FieldName>
|
||||
/**
|
||||
* Authorization for an individual field. Returning "true" or "Promise<true>" means the field can be
|
||||
* accessed. Returning "false" or "Promise<false>" will respond with a "Not Authorized" error for the
|
||||
* field. Returning or throwing an error will also prevent the resolver from executing.
|
||||
*/
|
||||
authorize?: FieldAuthorizeResolver<TypeName, FieldName>
|
||||
/**
|
||||
* The nullability guard can be helpful, but is also a potentially expensive operation for lists. We need
|
||||
* to iterate the entire list to check for null items to guard against. Set this to true to skip the null
|
||||
* guard on a specific field if you know there's no potential for unsafe types.
|
||||
*/
|
||||
skipNullGuard?: boolean
|
||||
/**
|
||||
* Whether the type can be null
|
||||
*
|
||||
* @default (depends on whether nullability is configured in type or schema)
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
nullable?: boolean
|
||||
/**
|
||||
* Whether the type is list of values, or just a single value. If list is true, we assume the type is a
|
||||
* list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether
|
||||
* the type is required (non-null), where true = nonNull, false = nullable.
|
||||
*
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
list?: true | boolean[]
|
||||
/**
|
||||
* Whether the type should be non null, `required: true` = `nullable: false`
|
||||
*
|
||||
* @default (depends on whether nullability is configured in type or schema)
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
required?: boolean
|
||||
}
|
||||
interface NexusGenPluginInputFieldConfig<TypeName extends string, FieldName extends string> {
|
||||
/**
|
||||
* Whether the type can be null
|
||||
*
|
||||
* @default (depends on whether nullability is configured in type or schema)
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
nullable?: boolean
|
||||
/**
|
||||
* Whether the type is list of values, or just a single value. If list is true, we assume the type is a
|
||||
* list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether
|
||||
* the type is required (non-null), where true = nonNull, false = nullable.
|
||||
*
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
list?: true | boolean[]
|
||||
/**
|
||||
* Whether the type should be non null, `required: true` = `nullable: false`
|
||||
*
|
||||
* @default (depends on whether nullability is configured in type or schema)
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
required?: boolean
|
||||
}
|
||||
interface NexusGenPluginSchemaConfig {}
|
||||
interface NexusGenPluginArgConfig {
|
||||
/**
|
||||
* Whether the type can be null
|
||||
*
|
||||
* @default (depends on whether nullability is configured in type or schema)
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
nullable?: boolean
|
||||
/**
|
||||
* Whether the type is list of values, or just a single value. If list is true, we assume the type is a
|
||||
* list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether
|
||||
* the type is required (non-null), where true = nonNull, false = nullable.
|
||||
*
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
list?: true | boolean[]
|
||||
/**
|
||||
* Whether the type should be non null, `required: true` = `nullable: false`
|
||||
*
|
||||
* @default (depends on whether nullability is configured in type or schema)
|
||||
* @see declarativeWrappingPlugin
|
||||
*/
|
||||
required?: boolean
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2140,6 +2140,11 @@ graphql@^15.3.0:
|
|||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
growly@^1.2.0, growly@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||
|
|
@ -5070,10 +5075,10 @@ type-is@~1.6.16:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.18"
|
||||
|
||||
typescript@^3.9:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.4.9"
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
"name": "@graphql-nexus/example-swapi-example",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"start": "ts-node-dev --ignore-watch star-wars-typegen.ts --no-notify --transpileOnly --respawn ./src"
|
||||
"start": "ts-node-dev --ignore-watch star-wars-typegen.ts --no-notify --transpile-only --respawn ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"apollo-server": "^2.18.1",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-tools": "^4.0.7",
|
||||
"nexus": "^1.0.0",
|
||||
"ts-node-dev": "^1.0.0-pre.30"
|
||||
|
|
@ -15,6 +15,6 @@
|
|||
"jest": "^23.6.0",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-jest": "^24.1.0",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2097,6 +2097,11 @@ graphql@^15.3.0:
|
|||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
growly@^1.2.0, growly@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||
|
|
@ -5006,10 +5011,10 @@ type-is@~1.6.16:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.18"
|
||||
|
||||
typescript@^3.9:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.4.9"
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
"dependencies": {
|
||||
"apollo-server": "^2.18.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-tools": "^4.0.7",
|
||||
"nexus": "^1.0.0",
|
||||
"ts-node-dev": "1.0.0-pre.31",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^5.0.4"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as ts from 'typescript'
|
||||
import type * as ts from 'typescript'
|
||||
|
||||
export interface ContextType {
|
||||
source: ts.SourceFile
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { objectType, arg, list, nullable } from 'nexus'
|
||||
import { objectType, arg, list, nullable, core } from 'nexus'
|
||||
import ts from 'typescript'
|
||||
import { nodeType, functionLikeDeclaration, hasTypeParameters } from './mixins'
|
||||
import { filteredNodesList } from './utils'
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ export const SourceFile = objectType({
|
|||
t.list.field('statements', {
|
||||
type: 'Node',
|
||||
args: nodeSkipSyntax,
|
||||
resolve: (root, args) => filteredNodesList(args, Array.from(root.statements)),
|
||||
resolve: (root, args) => filteredNodesList<ts.Statement>(args, Array.from(root.statements)),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
@ -156,7 +157,7 @@ export const ClassDeclaration = objectType({
|
|||
t.list.field('members', {
|
||||
type: 'Node',
|
||||
args: nodeSkipSyntax,
|
||||
resolve: (root, args) => filteredNodesList(args, Array.from(root.members)),
|
||||
resolve: (root, args) => filteredNodesList<ts.ClassElement>(args, Array.from(root.members)),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { arg, interfaceType, list, nullable } from 'nexus'
|
||||
import { JSDoc, SyntaxKind } from 'typescript'
|
||||
import ts, { JSDoc, SyntaxKind } from 'typescript'
|
||||
import { allKnownNodes, syntaxKindFilter } from './utils'
|
||||
|
||||
const syntaxKindArgs = {
|
||||
|
|
@ -50,7 +50,7 @@ export const Node = interfaceType({
|
|||
if (!root.modifiers) {
|
||||
return null
|
||||
}
|
||||
return syntaxKindFilter(args, Array.from(root.modifiers))
|
||||
return syntaxKindFilter<ts.Modifier>(args, Array.from(root.modifiers))
|
||||
},
|
||||
})
|
||||
t.field('parent', { type: 'Node' })
|
||||
|
|
@ -78,7 +78,7 @@ export const JSDocInterface = interfaceType({
|
|||
resolve(root) {
|
||||
if ('jsDoc' in root) {
|
||||
// https://github.com/Microsoft/TypeScript/issues/19856
|
||||
return ((root as unknown) as { jsDoc: JSDoc[] }).jsDoc
|
||||
return (root as unknown as { jsDoc: JSDoc[] }).jsDoc
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export const TupleTypeNode = objectType({
|
|||
name: 'TupleTypeNode',
|
||||
definition(t) {
|
||||
nodeType(t)
|
||||
t.list.field('elementTypes', { type: 'Node' })
|
||||
t.list.field('elements', { type: 'Node' })
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,72 +1,69 @@
|
|||
import { GraphQLSchema, isObjectType, GraphQLResolveInfo } from "graphql";
|
||||
import ts from "typescript";
|
||||
import { NexusGenArgTypes } from "../ts-ast-reader-typegen";
|
||||
import { GraphQLSchema, isObjectType, GraphQLResolveInfo } from 'graphql'
|
||||
import ts from 'typescript'
|
||||
import { NexusGenArgTypes } from '../ts-ast-reader-typegen'
|
||||
|
||||
export function convertTsEnum(toConvert: any) {
|
||||
const converted: { [key: string]: number } = {};
|
||||
const converted: { [key: string]: number } = {}
|
||||
Object.keys(toConvert).forEach((key) => {
|
||||
if (/^[_a-zA-Z][_a-zA-Z0-9]*$/.test(key)) {
|
||||
converted[key] = toConvert[key];
|
||||
converted[key] = toConvert[key]
|
||||
}
|
||||
});
|
||||
return converted;
|
||||
})
|
||||
return converted
|
||||
}
|
||||
|
||||
const knownNodesMap = new WeakMap<GraphQLSchema, Set<string>>();
|
||||
const knownNodesMap = new WeakMap<GraphQLSchema, Set<string>>()
|
||||
|
||||
export function allKnownNodes(schema: GraphQLSchema) {
|
||||
let knownNodes = knownNodesMap.get(schema);
|
||||
let knownNodes = knownNodesMap.get(schema)
|
||||
if (!knownNodes) {
|
||||
const fields = schema.getTypeMap();
|
||||
knownNodes = new Set();
|
||||
const fields = schema.getTypeMap()
|
||||
knownNodes = new Set()
|
||||
Object.keys(fields).forEach((key) => {
|
||||
const field = fields[key];
|
||||
if (
|
||||
isObjectType(field) &&
|
||||
Boolean(field.getInterfaces().find((i) => i.name === "Node"))
|
||||
) {
|
||||
knownNodes!.add(field.name);
|
||||
const field = fields[key]
|
||||
if (isObjectType(field) && Boolean(field.getInterfaces().find((i) => i.name === 'Node'))) {
|
||||
knownNodes!.add(field.name)
|
||||
}
|
||||
});
|
||||
knownNodesMap.set(schema, knownNodes);
|
||||
})
|
||||
knownNodesMap.set(schema, knownNodes)
|
||||
}
|
||||
return knownNodes!;
|
||||
return knownNodes!
|
||||
}
|
||||
|
||||
export const filteredNodesList = <T extends ts.Node>(
|
||||
args: NexusGenArgTypes["SourceFile"]["statements"],
|
||||
args: NexusGenArgTypes['SourceFile']['statements'],
|
||||
nodes: T[]
|
||||
) => {
|
||||
const { skip, only } = args;
|
||||
): T[] => {
|
||||
const { skip, only } = args
|
||||
if ((skip && skip.length) || (only && only.length)) {
|
||||
return nodes.filter((node: ts.Node) => {
|
||||
if (skip && skip.length > 0 && skip.indexOf(node.kind) !== -1) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (only && only.length > 0 && only.indexOf(node.kind) === -1) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
return node;
|
||||
});
|
||||
return node
|
||||
})
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
return nodes
|
||||
}
|
||||
|
||||
export const syntaxKindFilter = <T extends { kind: ts.SyntaxKind }>(
|
||||
args: NexusGenArgTypes["Node"]["modifiers"],
|
||||
args: NexusGenArgTypes['Node']['modifiers'],
|
||||
items: T[]
|
||||
) => {
|
||||
const { skip, only } = args;
|
||||
const { skip, only } = args
|
||||
if ((only && only.length) || (skip && skip.length)) {
|
||||
return items.filter((item) => {
|
||||
if (only && only.length && only.indexOf(item.kind) === -1) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
if (skip && skip.length && skip.indexOf(item.kind) !== -1) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
return item;
|
||||
});
|
||||
return item
|
||||
})
|
||||
}
|
||||
return items;
|
||||
};
|
||||
return items
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1113,6 +1113,11 @@ graphql@^15.3.0:
|
|||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
growly@^1.2.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||
|
|
@ -2164,10 +2169,10 @@ type-is@~1.6.16:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.18"
|
||||
|
||||
typescript@^3.9:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@
|
|||
"@prisma/client": "2.14.0",
|
||||
"apollo-server-express": "^2.19.1",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"nexus": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@prisma/cli": "^2.14.0",
|
||||
"prettier": "2.2.1",
|
||||
"ts-node-dev": "^1.0.0-pre.62",
|
||||
"typescript": "^4.0.2"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1015,6 +1015,11 @@ graphql@^15.3.0:
|
|||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.4.0.tgz#e459dea1150da5a106486ba7276518b5295a4347"
|
||||
integrity sha512-EB3zgGchcabbsU9cFe1j+yxdzKQKAbGUWRb13DsrsMN1yyfmmIq+2+L5MqVWcDCE4V89R5AyUOi7sMOGxdsYtA==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
has-symbols@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
||||
|
|
@ -1814,10 +1819,10 @@ type-is@^1.6.16, type-is@~1.6.17, type-is@~1.6.18:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
typescript@^4.0.2:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
|
||||
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@
|
|||
"trailingComma": "all"
|
||||
},
|
||||
"dependencies": {
|
||||
"graphql": "^15.3.0",
|
||||
"graphql": "^16.3.0",
|
||||
"nexus": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@now/node": "^1.0.1",
|
||||
"@types/node": "^12.11.1",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.9"
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ diff@^4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff"
|
||||
integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==
|
||||
|
||||
graphql@^15.3.0:
|
||||
version "15.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278"
|
||||
integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
iterall@^1.3.0:
|
||||
version "1.3.0"
|
||||
|
|
@ -81,10 +81,10 @@ tslib@^2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
|
||||
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
|
||||
|
||||
typescript@^3.9:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.5.5:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
yn@^3.0.0:
|
||||
version "3.1.1"
|
||||
|
|
|
|||
28
package.json
28
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "nexus",
|
||||
"version": "0.0.0-dripip",
|
||||
"version": "1.3.0",
|
||||
"description": "Scalable, strongly typed GraphQL schema development",
|
||||
"keywords": [
|
||||
"graphql",
|
||||
|
|
@ -46,7 +46,6 @@
|
|||
"release:pr": "dripip pr",
|
||||
"release:preview": "dripip preview",
|
||||
"release:stable": "dripip stable",
|
||||
"pretest": "yarn patch-package",
|
||||
"test": "yarn test:types && jest --testTimeout 10000",
|
||||
"test:ci": "yarn test:types && jest --maxWorkers 2 --coverage --testTimeout 10000",
|
||||
"test:debug": "node --inspect-brk $(yarn bin)/jest -i --watch",
|
||||
|
|
@ -62,12 +61,10 @@
|
|||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,js,graphql,json,css,md}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
"prettier --write"
|
||||
],
|
||||
"*package.json": [
|
||||
"sort-package-json",
|
||||
"git add"
|
||||
"sort-package-json"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -82,29 +79,28 @@
|
|||
"@types/prettier": "^1.18.3",
|
||||
"@typescript-eslint/eslint-plugin": "2.7.0",
|
||||
"dripip": "^0.10.0",
|
||||
"esbuild": "^0.14.48",
|
||||
"eslint": "^6.6.0",
|
||||
"get-port": "^5.1.1",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql-relay": "^0.6.0",
|
||||
"graphql-scalars": "^1.2.6",
|
||||
"graphql": "^16.3.0",
|
||||
"graphql-relay": "^0.10.0",
|
||||
"graphql-scalars": "^1.14.1",
|
||||
"gulp": "4.0.2",
|
||||
"husky": "^1.1.2",
|
||||
"jest": "^26.6.3",
|
||||
"jest-watch-typeahead": "^0.6.1",
|
||||
"lint-staged": "^7.3.0",
|
||||
"patch-package": "6.2.2",
|
||||
"prettier": "^2.2.1",
|
||||
"prettier-plugin-jsdoc": "^0.2.5",
|
||||
"lint-staged": "^12.4.1",
|
||||
"prettier": "^2.5.1",
|
||||
"sort-package-json": "^1.22.1",
|
||||
"ts-jest": "^26.4.4",
|
||||
"ts-morph": "^8.2.0",
|
||||
"ts-morph": "^13.0.3",
|
||||
"ts-node": "^9.0.0",
|
||||
"tsd": "^0.13.1",
|
||||
"tslint": "^5.11.0",
|
||||
"tslint-config-prettier": "^1.15.0",
|
||||
"typescript": "^4.1.2"
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^14.5.8 || 15.x"
|
||||
"graphql": "15.x || 16.x"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
diff --git a/node_modules/prettier-plugin-jsdoc/lib/parser.js b/node_modules/prettier-plugin-jsdoc/lib/parser.js
|
||||
index 85b9e5d..3b98730 100644
|
||||
--- a/node_modules/prettier-plugin-jsdoc/lib/parser.js
|
||||
+++ b/node_modules/prettier-plugin-jsdoc/lib/parser.js
|
||||
@@ -12,7 +12,7 @@ const stringify_1 = require("./stringify");
|
||||
/** @link https://prettier.io/docs/en/api.html#custom-parser-api} */
|
||||
exports.getParser = (parser) => function jsdocParser(text, parsers, options) {
|
||||
const ast = parser(text, parsers, options);
|
||||
- if (!options.jsdocParser) {
|
||||
+ if (!options || !options.jsdocParser) {
|
||||
return ast;
|
||||
}
|
||||
/**
|
||||
881
src/builder.ts
881
src/builder.ts
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,9 @@
|
|||
// The "core" is used as a namespace to re-export everything,
|
||||
// For anyone who wants to use the internals
|
||||
export * from './builder'
|
||||
export * from './makeSchema'
|
||||
export * from './definitions/args'
|
||||
export * from './definitions/directive'
|
||||
export * from './definitions/decorateType'
|
||||
export * from './definitions/definitionBlocks'
|
||||
export * from './definitions/enumType'
|
||||
|
|
@ -20,12 +22,14 @@ export * from './definitions/queryType'
|
|||
export * from './definitions/scalarType'
|
||||
export * from './definitions/subscriptionField'
|
||||
export * from './definitions/subscriptionType'
|
||||
export * from './definitions/nexusMeta'
|
||||
export * from './definitions/unionType'
|
||||
export * from './definitions/wrapping'
|
||||
export * from './definitions/_types'
|
||||
export * from './dynamicMethod'
|
||||
export * from './plugin'
|
||||
export * from './plugins'
|
||||
export * from './rebuildType'
|
||||
export * from './sdlConverter'
|
||||
export * from './typegenAutoConfig'
|
||||
export * from './typegenFormatPrettier'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {
|
||||
import type {
|
||||
GraphQLCompositeType,
|
||||
GraphQLEnumType,
|
||||
GraphQLFieldConfig,
|
||||
|
|
@ -13,19 +13,21 @@ import {
|
|||
GraphQLSchema,
|
||||
GraphQLUnionType,
|
||||
} from 'graphql'
|
||||
import {
|
||||
import type { NexusDirectiveConfig } from '../core'
|
||||
import type {
|
||||
NexusFieldExtension,
|
||||
NexusInputObjectTypeExtension,
|
||||
NexusInterfaceTypeExtension,
|
||||
NexusObjectTypeExtension,
|
||||
NexusSchemaExtension,
|
||||
} from '../extensions'
|
||||
import * as AbstractTypes from '../typegenAbstractTypes'
|
||||
import { RequiredDeeply } from '../typeHelpersInternal'
|
||||
import type * as AbstractTypes from '../typegenAbstractTypes'
|
||||
import type { RequiredDeeply } from '../typeHelpersInternal'
|
||||
|
||||
export { AbstractTypes }
|
||||
export type { AbstractTypes }
|
||||
|
||||
export type Maybe<T> = T | null
|
||||
/** Conveniently represents flow's "Maybe" type https://flow.org/en/docs/types/maybe/ */
|
||||
export type Maybe<T> = null | undefined | T
|
||||
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
|
||||
|
||||
|
|
@ -52,6 +54,8 @@ export enum NexusTypes {
|
|||
PrintedGenTypingImport = 'PrintedGenTypingImport',
|
||||
Scalar = 'Scalar',
|
||||
Union = 'Union',
|
||||
Directive = 'Directive',
|
||||
DirectiveUse = 'DirectiveUse',
|
||||
}
|
||||
|
||||
export interface DeprecationInfo {
|
||||
|
|
@ -154,8 +158,13 @@ export type NexusGraphQLInterfaceTypeConfig = WithExt<
|
|||
NexusInterfaceTypeExtension
|
||||
> & { interfaces: () => GraphQLInterfaceType[] }
|
||||
|
||||
export type NexusGraphQLSchema = Omit<GraphQLSchema, 'extensions'> & {
|
||||
extensions: { nexus: NexusSchemaExtension }
|
||||
export type NexusGraphQLDirectiveConfig = WithExt<NexusDirectiveConfig, NexusInputObjectTypeExtension>
|
||||
|
||||
export interface NexusGraphQLSchema extends GraphQLSchema {
|
||||
extensions: {
|
||||
nexus: NexusSchemaExtension
|
||||
[attributeName: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type NexusFeaturesInput = {
|
||||
|
|
@ -178,12 +187,9 @@ export type NexusFeaturesInput = {
|
|||
* using multiple strategies](https://nxs.li/guides/abstract-types/using-multiple-strategies) as there are
|
||||
* a few quirks to be aware of.
|
||||
*
|
||||
* @default
|
||||
* {
|
||||
* resolveType: true,
|
||||
* @default {resolveType: true,
|
||||
* __typename: false
|
||||
* isTypeOf: false,
|
||||
* }
|
||||
* isTypeOf: false,}
|
||||
*/
|
||||
abstractTypeStrategies?: {
|
||||
/**
|
||||
|
|
@ -212,8 +218,8 @@ export type NexusFeaturesInput = {
|
|||
* static typing. So as long as you are not ignoring static errors related to Nexus' abstract type type
|
||||
* checks then you then you should still have a safe implementation.
|
||||
*
|
||||
* Furthermore another effect is that statically the other strategies will not appear to be _required_,
|
||||
* but instead _optional_, while only this one will appear required. However, upon implementing any of
|
||||
* Furthermore another effect is that statically the other strategies will not appear to be *required*,
|
||||
* but instead *optional*, while only this one will appear required. However, upon implementing any of
|
||||
* the other strategies, this one will not longer be required. This quirk is explained in the guide
|
||||
* section about [using multiple strategies](https://nxs.li/guides/abstract-types/using-multiple-strategies).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { GraphQLScalarTypeConfig } from 'graphql'
|
||||
import { AllInputTypes, GetGen2 } from '../typegenTypeHelpers'
|
||||
import { AllNexusArgsDefs, AllNexusInputTypeDefs } from './wrapping'
|
||||
import { NexusTypes, withNexusSymbol } from './_types'
|
||||
import type { GraphQLScalarTypeConfig } from 'graphql'
|
||||
import type { AllInputTypes, GetGen2 } from '../typegenTypeHelpers'
|
||||
import type { Directives } from './directive'
|
||||
import type { AllNexusArgsDefs, AllNexusInputTypeDefs } from './wrapping'
|
||||
import { Maybe, NexusTypes, withNexusSymbol } from './_types'
|
||||
|
||||
export type ArgsRecord = Record<string, AllNexusArgsDefs>
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ export type CommonArgConfig = {
|
|||
* // ): [Int]
|
||||
* // }
|
||||
*/
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
|
||||
/**
|
||||
* Data that will be added to the arg-level [extensions field on the graphql-js type def
|
||||
|
|
@ -47,13 +48,20 @@ export type CommonArgConfig = {
|
|||
* graphql-js based tools which rely on looking for special data here.
|
||||
*/
|
||||
extensions?: GraphQLScalarTypeConfig<any, any>['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the arg definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
} & NexusGenPluginArgConfig
|
||||
|
||||
export interface ScalarArgConfig<T> extends CommonArgConfig {
|
||||
/**
|
||||
* The default value for this argument when ***none*** is given by the client.
|
||||
*
|
||||
* Note that _null_ is still considered something meaning if the client gives an explicit null that will
|
||||
* Note that *null* is still considered something meaning if the client gives an explicit null that will
|
||||
* prevent the default from activating. This is why the type of an arg with a default value in the resolver
|
||||
* includes "undefined | null".
|
||||
*
|
||||
|
|
@ -86,12 +94,10 @@ export interface NexusArgConfig<T extends string> extends NexusAsArgConfig<T> {
|
|||
*
|
||||
* Types may be expressed in one of three ways:
|
||||
*
|
||||
* 1. As string literals matching the name of a builtin scalar.
|
||||
*
|
||||
* 2. As string literals matching the name of another type. Thanks to [Nexus' reflection
|
||||
* system](https://nxs.li/guides/reflection) this is typesafe and autocompletable.
|
||||
*
|
||||
* 3. As references to other enums or input object type definitions.
|
||||
* 1. As string literals matching the name of a builtin scalar. 2. As string literals matching the name of
|
||||
* another type. Thanks to [Nexus' reflection
|
||||
* system](https://nxs.li/guides/reflection) this is typesafe and autocompletable. 3. As references to other
|
||||
* enums or input object type definitions.
|
||||
*
|
||||
* Type modifier helpers like list() may also be used and in turn accept one of the three methods listed above.
|
||||
*
|
||||
|
|
@ -193,8 +199,7 @@ export function arg<T extends string>(config: NexusArgConfig<T>) {
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field
|
||||
* for details.
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field for details.
|
||||
*/
|
||||
export function stringArg(config?: ScalarArgConfig<string>) {
|
||||
return arg({ type: 'String', ...config })
|
||||
|
|
@ -224,8 +229,7 @@ export function stringArg(config?: ScalarArgConfig<string>) {
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field
|
||||
* for details.
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field for details.
|
||||
*/
|
||||
export function intArg(config?: ScalarArgConfig<number>) {
|
||||
return arg({ type: 'Int', ...config })
|
||||
|
|
@ -255,8 +259,7 @@ export function intArg(config?: ScalarArgConfig<number>) {
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field
|
||||
* for details.
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field for details.
|
||||
*/
|
||||
export function floatArg(config?: ScalarArgConfig<number>) {
|
||||
return arg({ type: 'Float', ...config })
|
||||
|
|
@ -286,8 +289,7 @@ export function floatArg(config?: ScalarArgConfig<number>) {
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field
|
||||
* for details.
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field for details.
|
||||
*/
|
||||
export function idArg(config?: ScalarArgConfig<string>) {
|
||||
return arg({ type: 'ID', ...config })
|
||||
|
|
@ -317,8 +319,7 @@ export function idArg(config?: ScalarArgConfig<string>) {
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field
|
||||
* for details.
|
||||
* @param config Configuration for the argument like its description. See jsdoc on each config field for details.
|
||||
*/
|
||||
export function booleanArg(config?: ScalarArgConfig<boolean>) {
|
||||
return arg({ type: 'Boolean', ...config })
|
||||
|
|
|
|||
|
|
@ -1,22 +1,18 @@
|
|||
import { GraphQLNamedType } from 'graphql'
|
||||
import { SourceTypingDef } from './_types'
|
||||
import type { GraphQLNamedType } from 'graphql'
|
||||
import type { SourceTypingDef } from './_types'
|
||||
|
||||
export interface TypeExtensionConfig {
|
||||
asNexusMethod?: string
|
||||
sourceType?: SourceTypingDef
|
||||
}
|
||||
|
||||
export type NexusTypeExtensions = {
|
||||
nexus: TypeExtensionConfig
|
||||
}
|
||||
|
||||
export function decorateType<T extends GraphQLNamedType>(type: T, config: TypeExtensionConfig): T {
|
||||
type.extensions = {
|
||||
...type.extensions,
|
||||
nexus: {
|
||||
asNexusMethod: config.asNexusMethod,
|
||||
sourceType: config.sourceType,
|
||||
...Object(type.extensions?.nexus),
|
||||
...config,
|
||||
},
|
||||
}
|
||||
return type as any
|
||||
return type
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,27 @@
|
|||
import { GraphQLFieldConfig, GraphQLFieldResolver, GraphQLInputFieldConfig } from 'graphql'
|
||||
import type { GraphQLFieldConfig, GraphQLFieldResolver, GraphQLInputFieldConfig } from 'graphql'
|
||||
import { messages } from '../messages'
|
||||
import { AllInputTypes, FieldResolver, GetGen, GetGen3, HasGen3, NeedsResolver } from '../typegenTypeHelpers'
|
||||
import { ArgsRecord } from './args'
|
||||
import { AllNexusInputTypeDefs, AllNexusOutputTypeDefs, NexusWrapKind } from './wrapping'
|
||||
import { BaseScalars } from './_types'
|
||||
import type {
|
||||
AllInputTypes,
|
||||
FieldResolver,
|
||||
GetGen,
|
||||
GetGen3,
|
||||
HasGen3,
|
||||
NeedsResolver,
|
||||
} from '../typegenTypeHelpers'
|
||||
import type { ArgsRecord } from './args'
|
||||
import type { Directives } from './directive'
|
||||
import type { NexusMetaType } from './nexusMeta'
|
||||
import type { AllNexusInputTypeDefs, AllNexusOutputTypeDefs, NexusWrapKind } from './wrapping'
|
||||
import type { BaseScalars, Maybe } from './_types'
|
||||
|
||||
export interface CommonFieldConfig {
|
||||
//todo
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string
|
||||
//todo
|
||||
description?: Maybe<string>
|
||||
/**
|
||||
* Info about a field deprecation. Formatted as a string and provided with the deprecated directive on
|
||||
* field/enum types and as a comment on input fields.
|
||||
*/
|
||||
deprecation?: string // | DeprecationInfo;
|
||||
deprecation?: Maybe<string> // | DeprecationInfo;
|
||||
}
|
||||
|
||||
export type CommonOutputFieldConfig<TypeName extends string, FieldName extends string> = CommonFieldConfig & {
|
||||
|
|
@ -67,7 +74,7 @@ export type CommonOutputFieldConfig<TypeName extends string, FieldName extends s
|
|||
* },
|
||||
* })
|
||||
*/
|
||||
args?: ArgsRecord
|
||||
args?: Maybe<ArgsRecord>
|
||||
/**
|
||||
* Data that will be added to the field-level [extensions field on the graphql-js type def
|
||||
* instances](https://github.com/graphql/graphql-js/issues/1527) resulting from makeSchema. Useful for some
|
||||
|
|
@ -97,8 +104,43 @@ export type CommonOutputFieldConfig<TypeName extends string, FieldName extends s
|
|||
* })
|
||||
*/
|
||||
extensions?: GraphQLFieldConfig<any, any>['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the output field definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
/**
|
||||
* Defines a typing for the field, overriding the default behavior to default to the scalar,
|
||||
* and omit the field if a resolver exists. Most useful in situations where we have a resolver
|
||||
* but we still want the field defined on the output type.
|
||||
*
|
||||
* @example
|
||||
* sourceType: 'string | number'
|
||||
*/
|
||||
sourceType?: string | FieldSourceType | NamedFieldSourceType[]
|
||||
} & NexusGenPluginFieldConfig<TypeName, FieldName>
|
||||
|
||||
export interface FieldSourceType {
|
||||
/**
|
||||
* String representing the TypeScript type output as the value
|
||||
*/
|
||||
type: string
|
||||
/**
|
||||
* If true, marks the field as optional `?:`
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean
|
||||
}
|
||||
|
||||
export interface NamedFieldSourceType extends FieldSourceType {
|
||||
/**
|
||||
* Property name in the output TypeScript field
|
||||
*/
|
||||
name: string
|
||||
}
|
||||
|
||||
export type CommonInputFieldConfig<TypeName extends string, FieldName extends string> = CommonFieldConfig & {
|
||||
/** The default value for the field, if any */
|
||||
default?: GetGen3<'inputTypes', TypeName, FieldName>
|
||||
|
|
@ -108,7 +150,16 @@ export type CommonInputFieldConfig<TypeName extends string, FieldName extends st
|
|||
* graphql-js based tools which rely on looking for special data here.
|
||||
*/
|
||||
extensions?: GraphQLInputFieldConfig['extensions']
|
||||
} & NexusGenPluginFieldConfig<TypeName, FieldName>
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the input field definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
} & NexusGenPluginFieldConfig<TypeName, FieldName> &
|
||||
NexusGenPluginInputFieldConfig<TypeName, FieldName>
|
||||
|
||||
export interface OutputScalarConfig<TypeName extends string, FieldName extends string>
|
||||
extends CommonOutputFieldConfig<TypeName, FieldName> {
|
||||
/**
|
||||
|
|
@ -119,11 +170,7 @@ export interface OutputScalarConfig<TypeName extends string, FieldName extends s
|
|||
* Every field has a resolver and they are the basis for resolving queries at runtime. You do not need to
|
||||
* explicitly implement every resolver however. If the [source typing](https://nxs.li/guides/backing-types) includes:
|
||||
*
|
||||
* 1. A field whose name matches this one
|
||||
*
|
||||
* 2. And whose type is compatible
|
||||
*
|
||||
* 3. And is a scalar
|
||||
* 1. A field whose name matches this one 2. And whose type is compatible 3. And is a scalar
|
||||
*
|
||||
* ...then the default resolver will be available, whose behaviour is to simply return that field from the
|
||||
* received source type.
|
||||
|
|
@ -141,23 +188,23 @@ export interface OutputScalarConfig<TypeName extends string, FieldName extends s
|
|||
* })
|
||||
*
|
||||
* @param source The [source data](https://nxs.li/guides/source-types) for the GraphQL object that this
|
||||
* field belongs to, unless this is a root
|
||||
* field (any field on a [root
|
||||
* operation type](https://spec.graphql.org/June2018/#sec-Root-Operation-Types): Query, Mutation,
|
||||
* Subscription), in which case there is no source data and this will be undefined.
|
||||
* field belongs to, unless this is a root field (any field on a [root operation
|
||||
* type](https://spec.graphql.org/June2018/#sec-Root-Operation-Types): Query, Mutation, Subscription),
|
||||
* in which case there is no source data and this will be undefined.
|
||||
* @param args If you have defined arguments on this field then this parameter will contain any arguments
|
||||
* passed by the client. If you specified default values for any arguments and the client did not explicitly
|
||||
* pass _any_ value (including null) for those arguments then you will see the defaults here.
|
||||
* passed by the client. If you specified default values for any arguments and the client did not
|
||||
* explicitly pass *any* value (including null) for those arguments then you will see the defaults here.
|
||||
*
|
||||
* Note that thanks to [Nexus' reflection system](https://nxs.li/guides/reflection) this parameter's type
|
||||
* will always be type safe.
|
||||
* @param context The context data for this request.
|
||||
*
|
||||
* The context data is typically a singleton scoped to the lifecycle of the request. This means created at
|
||||
* the beginning of a request and then passed to all the resolvers that execute while resolving the request.
|
||||
* It is often used to store information like the current user making the request. Nexus is not responsible
|
||||
* for this however. That is typically something you'll do with e.g. [Mercurius](https://mercurius.dev) or
|
||||
* [Apollo Server](https://apollographql.com/docs/apollo-server/api/apollo-server).
|
||||
* the beginning of a request and then passed to all the resolvers that execute while resolving the
|
||||
* request. It is often used to store information like the current user making the request. Nexus is
|
||||
* not responsible for this however. That is typically something you'll do with e.g.
|
||||
* [Mercurius](https://mercurius.dev) or [Apollo
|
||||
* Server](https://apollographql.com/docs/apollo-server/api/apollo-server).
|
||||
*
|
||||
* Note that the type here will be whatever you have specified for "contextType" in your makeSchema configuration.
|
||||
* @param info The GraphQL resolve info.
|
||||
|
|
@ -168,8 +215,8 @@ export interface OutputScalarConfig<TypeName extends string, FieldName extends s
|
|||
resolve?: FieldResolver<TypeName, FieldName>
|
||||
}
|
||||
|
||||
export interface NexusOutputFieldConfig<TypeName extends string, FieldName extends string>
|
||||
extends OutputScalarConfig<TypeName, FieldName> {
|
||||
// prettier-ignore
|
||||
export interface NexusOutputFieldConfig<TypeName extends string, FieldName extends string> extends OutputScalarConfig<TypeName, FieldName> {
|
||||
/**
|
||||
* [GraphQL 2018 Spec](https://spec.graphql.org/June2018/#sec-Types)
|
||||
*
|
||||
|
|
@ -263,7 +310,15 @@ export interface NexusOutputFieldConfig<TypeName extends string, FieldName exten
|
|||
* },
|
||||
* })
|
||||
*/
|
||||
type: GetGen<'allOutputTypes', string> | AllNexusOutputTypeDefs
|
||||
type: GetGen<'allOutputTypes', string> | AllNexusOutputTypeDefs | NexusMetaType
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export interface NexusOutputFieldConfigWithName<TypeName extends string, FieldName extends string> extends NexusOutputFieldConfig<TypeName, FieldName> {
|
||||
/**
|
||||
* The name of this field. Must conform to the regex pattern: [_A-Za-z][_0-9A-Za-z]*
|
||||
*/
|
||||
name: FieldName
|
||||
}
|
||||
|
||||
export type NexusOutputFieldDef = NexusOutputFieldConfig<string, any> & {
|
||||
|
|
@ -291,14 +346,21 @@ export type ScalarOutConfig<TypeName extends string, FieldName extends string> =
|
|||
}
|
||||
: OutputScalarConfig<TypeName, FieldName>
|
||||
|
||||
export type FieldOutConfig<TypeName extends string, FieldName extends string> = NeedsResolver<
|
||||
TypeName,
|
||||
FieldName
|
||||
> extends true
|
||||
? NexusOutputFieldConfig<TypeName, FieldName> & {
|
||||
resolve: FieldResolver<TypeName, FieldName>
|
||||
}
|
||||
: NexusOutputFieldConfig<TypeName, FieldName>
|
||||
// prettier-ignore
|
||||
export type FieldOutConfig<TypeName extends string, FieldName extends string> =
|
||||
NeedsResolver<TypeName, FieldName> extends true
|
||||
? NexusOutputFieldConfig<TypeName, FieldName> & {
|
||||
resolve: FieldResolver<TypeName, FieldName>
|
||||
}
|
||||
: NexusOutputFieldConfig<TypeName, FieldName>
|
||||
|
||||
// prettier-ignore
|
||||
export type FieldOutConfigWithName<TypeName extends string, FieldName extends string> =
|
||||
NeedsResolver<TypeName, FieldName> extends true
|
||||
? NexusOutputFieldConfigWithName<TypeName, FieldName> & {
|
||||
resolve: FieldResolver<TypeName, FieldName>
|
||||
}
|
||||
: NexusOutputFieldConfigWithName<TypeName, FieldName>
|
||||
|
||||
export interface OutputDefinitionBuilder {
|
||||
typeName: string
|
||||
|
|
@ -406,7 +468,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* Guide](https://nexusjs.org/docs/guides/nullability) | [GraphQL 2018
|
||||
* Spec](https://spec.graphql.org/June2018/#sec-Type-System.Non-Null)
|
||||
*
|
||||
* Chain this property to _unwrap_ the right-hand-side type (the field type or a list) of a Non-Null type.
|
||||
* Chain this property to *unwrap* the right-hand-side type (the field type or a list) of a Non-Null type.
|
||||
*
|
||||
* In Nexus output types are nullable by default so this is only useful when you have changed your
|
||||
* nonNullDefaults configuration either globally in your makeSchema config or at the type definition level
|
||||
|
|
@ -454,7 +516,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
*
|
||||
* This is a shorthand equivalent to:
|
||||
*
|
||||
* ` t.field('...', { type: boolean() }) `
|
||||
* `t.field('...', { type: boolean() })`
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
|
|
@ -471,14 +533,10 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* This parameter is optional if no resolver is required. No resolver is required if the [source
|
||||
* typing](https://nxs.li/guides/backing-types):
|
||||
*
|
||||
* 1. Has a field whose name matches this one
|
||||
* 1. Has a field whose name matches this one 2. And whose type is compatible 3. And is a scalar
|
||||
*
|
||||
* 2. And whose type is compatible
|
||||
*
|
||||
* 3. And is a scalar
|
||||
*
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field from the
|
||||
* received source type.
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field
|
||||
* from the received source type.
|
||||
*/
|
||||
boolean<FieldName extends string>(name: FieldName, ...config: ScalarOutSpread<TypeName, FieldName>) {
|
||||
this.addScalarField(name, 'Boolean', config)
|
||||
|
|
@ -496,7 +554,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
*
|
||||
* This is a shorthand, equivalent to:
|
||||
*
|
||||
* ` t.field('...', { type: string() }) `
|
||||
* `t.field('...', { type: string() })`
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
|
|
@ -513,14 +571,10 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* This parameter is optional if no resolver is required. No resolver is required if the [source
|
||||
* typing](https://nxs.li/guides/backing-types):
|
||||
*
|
||||
* 1. Has a field whose name matches this one
|
||||
* 1. Has a field whose name matches this one 2. And whose type is compatible 3. And is a scalar
|
||||
*
|
||||
* 2. And whose type is compatible
|
||||
*
|
||||
* 3. And is a scalar
|
||||
*
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field from the
|
||||
* received source type.
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field
|
||||
* from the received source type.
|
||||
*/
|
||||
string<FieldName extends string>(name: FieldName, ...config: ScalarOutSpread<TypeName, FieldName>) {
|
||||
this.addScalarField(name, 'String', config)
|
||||
|
|
@ -539,7 +593,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
*
|
||||
* This is a shorthand, equivalent to:
|
||||
*
|
||||
* ` t.field('...', { type: id() }) `
|
||||
* `t.field('...', { type: id() })`
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
|
|
@ -556,14 +610,10 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* This parameter is optional if no resolver is required. No resolver is required if the [source
|
||||
* typing](https://nxs.li/guides/backing-types):
|
||||
*
|
||||
* 1. Has a field whose name matches this one
|
||||
* 1. Has a field whose name matches this one 2. And whose type is compatible 3. And is a scalar
|
||||
*
|
||||
* 2. And whose type is compatible
|
||||
*
|
||||
* 3. And is a scalar
|
||||
*
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field from the
|
||||
* received source type.
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field
|
||||
* from the received source type.
|
||||
*/
|
||||
id<FieldName extends string>(name: FieldName, ...config: ScalarOutSpread<TypeName, FieldName>) {
|
||||
this.addScalarField(name, 'ID', config)
|
||||
|
|
@ -580,7 +630,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
*
|
||||
* This is a shorthand equivalent to:
|
||||
*
|
||||
* ` t.field('...', { type: int() }) `
|
||||
* `t.field('...', { type: int() })`
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
|
|
@ -597,14 +647,10 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* This parameter is optional if no resolver is required. No resolver is required if the [source
|
||||
* typing](https://nxs.li/guides/backing-types):
|
||||
*
|
||||
* 1. Has a field whose name matches this one
|
||||
* 1. Has a field whose name matches this one 2. And whose type is compatible 3. And is a scalar
|
||||
*
|
||||
* 2. And whose type is compatible
|
||||
*
|
||||
* 3. And is a scalar
|
||||
*
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field from the
|
||||
* received source type.
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field
|
||||
* from the received source type.
|
||||
*/
|
||||
int<FieldName extends string>(name: FieldName, ...config: ScalarOutSpread<TypeName, FieldName>) {
|
||||
this.addScalarField(name, 'Int', config)
|
||||
|
|
@ -622,7 +668,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
*
|
||||
* This is a shorthand, equivalent to:
|
||||
*
|
||||
* ` t.field('...', { type: float() }) `
|
||||
* `t.field('...', { type: float() })`
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
|
|
@ -639,14 +685,10 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* This parameter is optional if no resolver is required. No resolver is required if the [source
|
||||
* typing](https://nxs.li/guides/backing-types):
|
||||
*
|
||||
* 1. Has a field whose name matches this one
|
||||
* 1. Has a field whose name matches this one 2. And whose type is compatible 3. And is a scalar
|
||||
*
|
||||
* 2. And whose type is compatible
|
||||
*
|
||||
* 3. And is a scalar
|
||||
*
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field from the
|
||||
* received source type.
|
||||
* ...in which case the default resolver will be available whose behaviour is to simply return that field
|
||||
* from the received source type.
|
||||
*/
|
||||
float<FieldName extends string>(name: FieldName, ...config: ScalarOutSpread<TypeName, FieldName>) {
|
||||
this.addScalarField(name, 'Float', config)
|
||||
|
|
@ -674,12 +716,44 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
* })
|
||||
*
|
||||
* @param name The name of this field. Must conform to the regex pattern: [_A-Za-z][_0-9A-Za-z]*
|
||||
* @param config The configuration for things like the field's type, its description, its arguments,
|
||||
* its resolver, and more. See jsdoc on each field within for details.
|
||||
* @param config The configuration for things like the field's type, its description, its arguments, its
|
||||
* resolver, and more. See jsdoc on each field within for details.
|
||||
*/
|
||||
field<FieldName extends string>(name: FieldName, config: FieldOutConfig<TypeName, FieldName>) {
|
||||
field<FieldName extends string>(name: FieldName, config: FieldOutConfig<TypeName, FieldName>): void
|
||||
/**
|
||||
* [GraphQL 2018 Spec](https://spec.graphql.org/June2018/#sec-Language.Fields)
|
||||
*
|
||||
* Define a field on this object.
|
||||
*
|
||||
* A field describes one discrete piece of information available to request within a [selection
|
||||
* set](https://spec.graphql.org/June2018/#sec-Selection-Sets). They are in fact most of what any selection
|
||||
* set will contain. Fields can be typed as scalars (marking the terminal point of a branch of a selection
|
||||
* set) or as other object types in your schema thus allowing you to model relationships between things.
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
* name: 'User',
|
||||
* definition(t) {
|
||||
* t.field({
|
||||
* name: 'id',
|
||||
* type: id(),
|
||||
* description: 'The unique identification number for this user',
|
||||
* })
|
||||
* },
|
||||
* })
|
||||
*
|
||||
* @param config The configuration for things like the field's type, its description, its arguments, its
|
||||
* resolver, and more. See jsdoc on each field within for details.
|
||||
*/
|
||||
field<FieldName extends string>(config: FieldOutConfigWithName<TypeName, FieldName>): void
|
||||
field<FieldName extends string>(
|
||||
...args:
|
||||
| [name: FieldName, config: FieldOutConfig<TypeName, FieldName>]
|
||||
| [config: FieldOutConfigWithName<TypeName, FieldName>]
|
||||
): void {
|
||||
const config = args.length === 2 ? { name: args[0], ...args[1] } : args[0]
|
||||
|
||||
this.typeBuilder.addField({
|
||||
name,
|
||||
...config,
|
||||
configFor: 'outputField',
|
||||
wrapping: this.wrapping,
|
||||
|
|
@ -718,11 +792,21 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
}
|
||||
}
|
||||
|
||||
export interface NexusInputFieldConfig<TypeName extends string, FieldName extends string>
|
||||
extends CommonInputFieldConfig<TypeName, FieldName> {
|
||||
/** TODO move the code below to definitionBlocks/input.ts Input */
|
||||
|
||||
// prettier-ignore
|
||||
export interface NexusInputFieldConfig<TypeName extends string, FieldName extends string> extends CommonInputFieldConfig<TypeName, FieldName> {
|
||||
type: AllInputTypes | AllNexusInputTypeDefs
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export interface NexusInputFieldConfigWithName<TypeName extends string, FieldName extends string> extends NexusInputFieldConfig<TypeName, FieldName> {
|
||||
/**
|
||||
* The name of this field. Must conform to the regex pattern: [_A-Za-z][_0-9A-Za-z]*
|
||||
*/
|
||||
name: FieldName
|
||||
}
|
||||
|
||||
export type NexusInputFieldDef = NexusInputFieldConfig<string, string> & {
|
||||
configFor: 'inputField'
|
||||
name: string
|
||||
|
|
@ -780,13 +864,17 @@ export class InputDefinitionBlock<TypeName extends string> {
|
|||
this.field(fieldName, { ...config, type: 'Float' })
|
||||
}
|
||||
|
||||
field<FieldName extends string>(config: NexusInputFieldConfigWithName<TypeName, FieldName>): void
|
||||
field<FieldName extends string>(name: FieldName, config: NexusInputFieldConfig<TypeName, FieldName>): void
|
||||
field<FieldName extends string>(
|
||||
fieldName: FieldName,
|
||||
fieldConfig: NexusInputFieldConfig<TypeName, FieldName>
|
||||
) {
|
||||
...args:
|
||||
| [FieldName, NexusInputFieldConfig<TypeName, FieldName>]
|
||||
| [NexusInputFieldConfigWithName<TypeName, FieldName>]
|
||||
): void {
|
||||
const config = args.length === 2 ? { name: args[0], ...args[1] } : args[0]
|
||||
|
||||
this.typeBuilder.addField({
|
||||
name: fieldName,
|
||||
...fieldConfig,
|
||||
...config,
|
||||
wrapping: this.wrapping,
|
||||
parentType: this.typeName,
|
||||
configFor: 'inputField',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
import {
|
||||
ArgumentNode,
|
||||
assertValidName,
|
||||
astFromValue,
|
||||
ASTKindToNode,
|
||||
DirectiveNode,
|
||||
GraphQLDirective,
|
||||
GraphQLDirectiveConfig,
|
||||
Kind,
|
||||
} from 'graphql'
|
||||
import { GetGen, GetGen2, NexusWrappedSymbol } from '../core'
|
||||
import type { MaybeReadonlyArray } from '../typeHelpersInternal'
|
||||
import { mapObj } from '../utils'
|
||||
import type { ArgsRecord } from './args'
|
||||
import { isNexusDirective, isNexusDirectiveUse } from './wrapping'
|
||||
import { Maybe, NexusTypes, withNexusSymbol } from './_types'
|
||||
|
||||
export type Directives = MaybeReadonlyArray<NexusDirectiveDef | GraphQLDirective | NexusDirectiveUse>
|
||||
|
||||
export const RequestDirectiveLocation = [
|
||||
/** Request Definitions */
|
||||
'QUERY',
|
||||
'MUTATION',
|
||||
'SUBSCRIPTION',
|
||||
'FIELD',
|
||||
'FRAGMENT_DEFINITION',
|
||||
'FRAGMENT_SPREAD',
|
||||
'INLINE_FRAGMENT',
|
||||
'VARIABLE_DEFINITION',
|
||||
] as const
|
||||
|
||||
export const SchemaDirectiveLocation = [
|
||||
/** Type System Definitions */
|
||||
'SCHEMA',
|
||||
'SCALAR',
|
||||
'OBJECT',
|
||||
'FIELD_DEFINITION',
|
||||
'ARGUMENT_DEFINITION',
|
||||
'INTERFACE',
|
||||
'UNION',
|
||||
'ENUM',
|
||||
'ENUM_VALUE',
|
||||
'INPUT_OBJECT',
|
||||
'INPUT_FIELD_DEFINITION',
|
||||
] as const
|
||||
|
||||
export type SchemaDirectiveLocationEnum = typeof SchemaDirectiveLocation[number]
|
||||
|
||||
export type RequestDirectiveLocationEnum = typeof RequestDirectiveLocation[number]
|
||||
|
||||
export interface NexusDirectiveConfig<DirectiveName extends string = string> {
|
||||
/** Name of the directive */
|
||||
name: DirectiveName
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string
|
||||
/** Valid locations that this directive may be used */
|
||||
locations: MaybeReadonlyArray<SchemaDirectiveLocationEnum | RequestDirectiveLocationEnum>
|
||||
/** Whether the directive can be repeated */
|
||||
isRepeatable?: Maybe<boolean> | undefined
|
||||
/**
|
||||
* [GraphQL.org Docs](https://graphql.github.io/learn/schema/#arguments) | [GraphQL 2018
|
||||
* Spec](https://spec.graphql.org/June2018/#sec-Language.Arguments)
|
||||
*
|
||||
* Define arguments for this directive.
|
||||
*
|
||||
* All directives in GraphQL can have arguments defined for them. Nexus provides a number of helpers for
|
||||
* defining arguments. All builtin GraphQL scalar types have helpers named "{scalarName}Arg" such as
|
||||
* "stringArg" and "intArg". You can also use type modifier helpers "[list](https://nxs.li/docs/api/list)"
|
||||
* "[nullable](https://nxs.li/docs/api/nullable)" and "[nonNull](https://nxs.li/docs/api/nonNull)". For
|
||||
* details about nonNull/nullable refer to the [nullability guide](https://nxs.li/guides/nullability).
|
||||
*
|
||||
* @example
|
||||
* export const TestValue = directive({
|
||||
* name: 'TestValue',
|
||||
* description: 'Denotes the value used when testing this type',
|
||||
* args: {
|
||||
* type: enumType({
|
||||
* name: 'TestValueType',
|
||||
* members: ['String', 'Int', 'Float', 'JSON'],
|
||||
* }),
|
||||
* value: stringArg(),
|
||||
* listLength: intArg(),
|
||||
* },
|
||||
* locations: ['FIELD_DEFINITION'],
|
||||
* })
|
||||
*/
|
||||
args?: ArgsRecord
|
||||
/** Data that will be added to the directive extensions field on the graphql-js type def instances */
|
||||
extensions?: GraphQLDirectiveConfig['extensions']
|
||||
}
|
||||
|
||||
export interface NexusDirectiveDef<DirectiveName extends string = string> {
|
||||
(...args: MaybeArgsFor<DirectiveName>): NexusDirectiveUse<DirectiveName>
|
||||
value: NexusDirectiveConfig
|
||||
[NexusWrappedSymbol]: 'Directive'
|
||||
}
|
||||
|
||||
export class NexusDirectiveUse<DirectiveName extends string = string> {
|
||||
constructor(
|
||||
readonly name: DirectiveName,
|
||||
readonly args: MaybeArgsFor<DirectiveName>[0] | undefined,
|
||||
readonly config?: NexusDirectiveConfig<DirectiveName>
|
||||
) {}
|
||||
}
|
||||
|
||||
withNexusSymbol(NexusDirectiveUse, NexusTypes.DirectiveUse)
|
||||
|
||||
/** Defines a directive, which can be used positionally when generating the SDL */
|
||||
export function directive<DirectiveName extends string>(
|
||||
config: NexusDirectiveConfig<DirectiveName>
|
||||
): NexusDirectiveDef<DirectiveName> {
|
||||
assertValidName(config.name)
|
||||
|
||||
config = Object.freeze(config)
|
||||
|
||||
function addDirective(...args: MaybeArgsFor<DirectiveName>) {
|
||||
return new NexusDirectiveUse(config.name, args[0], config)
|
||||
}
|
||||
|
||||
addDirective[NexusWrappedSymbol] = NexusTypes.Directive as 'Directive'
|
||||
|
||||
addDirective.value = config
|
||||
|
||||
return addDirective
|
||||
}
|
||||
|
||||
type MaybeArgsFor<DirectiveName extends string> = GetGen2<'directiveArgs', DirectiveName> extends object
|
||||
? [GetGen2<'directiveArgs', DirectiveName>]
|
||||
: []
|
||||
|
||||
export function addDirective<DirectiveName extends GetGen<'directives', string>>(
|
||||
directiveName: DirectiveName,
|
||||
...args: MaybeArgsFor<DirectiveName>
|
||||
) {
|
||||
return new NexusDirectiveUse<DirectiveName>(directiveName, args[0])
|
||||
}
|
||||
|
||||
const DirectiveASTKindMapping = {
|
||||
SCALAR: Kind.SCALAR_TYPE_DEFINITION, // 'ScalarTypeDefinition',
|
||||
SCHEMA: Kind.SCHEMA_DEFINITION, // 'SchemaDefinition',
|
||||
OBJECT: Kind.OBJECT_TYPE_DEFINITION, // 'ObjectTypeDefinition',
|
||||
FIELD_DEFINITION: Kind.FIELD_DEFINITION, // 'FieldDefinition',
|
||||
ARGUMENT_DEFINITION: Kind.INPUT_VALUE_DEFINITION, // 'InputValueDefinition',
|
||||
INTERFACE: Kind.INTERFACE_TYPE_DEFINITION, // 'InterfaceTypeDefinition',
|
||||
UNION: Kind.UNION_TYPE_DEFINITION, // 'UnionTypeDefinition',
|
||||
ENUM: Kind.ENUM_TYPE_DEFINITION, // 'EnumTypeDefinition',
|
||||
ENUM_VALUE: Kind.ENUM_VALUE_DEFINITION, // 'EnumValueDefinition',
|
||||
INPUT_OBJECT: Kind.INPUT_OBJECT_TYPE_DEFINITION, // 'InputObjectTypeDefinition',
|
||||
INPUT_FIELD_DEFINITION: Kind.INPUT_VALUE_DEFINITION, // 'InputValueDefinition',
|
||||
} as const
|
||||
|
||||
type DirectiveASTKindMapping = typeof DirectiveASTKindMapping
|
||||
export type DirectiveASTKinds = keyof DirectiveASTKindMapping
|
||||
|
||||
/**
|
||||
* Creates the ASTNode with the directives
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
export function maybeAddDirectiveUses<
|
||||
Kind extends DirectiveASTKinds,
|
||||
Result extends { astNode: ASTKindToNode[DirectiveASTKindMapping[Kind]] }
|
||||
>(
|
||||
kind: Kind,
|
||||
directiveUses: Directives | undefined,
|
||||
customDirectives: Record<string, GraphQLDirective>
|
||||
): Result | undefined {
|
||||
if (!directiveUses?.length) {
|
||||
return undefined
|
||||
}
|
||||
const seenDirectives = new Set<string>()
|
||||
return {
|
||||
astNode: {
|
||||
kind: DirectiveASTKindMapping[kind],
|
||||
directives: Array.from(directiveUses).map((d): DirectiveNode => {
|
||||
const directiveName = isNexusDirective(d) ? d.value.name : d.name
|
||||
const directiveDef = customDirectives[directiveName]
|
||||
if (seenDirectives.has(directiveName) && !directiveDef.isRepeatable) {
|
||||
throw new Error(`Cannot use directive ${directiveName} more than once in a row`)
|
||||
}
|
||||
assertValidDirectiveFor(kind, directiveDef)
|
||||
seenDirectives.add(directiveName)
|
||||
|
||||
const args = isNexusDirectiveUse(d) ? directiveArgs(d, directiveDef) : undefined
|
||||
|
||||
return {
|
||||
kind: Kind.DIRECTIVE,
|
||||
name: {
|
||||
kind: Kind.NAME,
|
||||
value: directiveName,
|
||||
},
|
||||
arguments: args,
|
||||
}
|
||||
}),
|
||||
},
|
||||
} as any
|
||||
}
|
||||
|
||||
function assertValidDirectiveFor(
|
||||
kind: DirectiveASTKinds,
|
||||
directiveDef: GraphQLDirective & { locations: readonly any[] } // any is a hack to make this work w/ v15 & 16
|
||||
) {
|
||||
if (!directiveDef.locations.includes(kind)) {
|
||||
throw new Error(`Directive ${directiveDef.name} cannot be applied to ${kind}`)
|
||||
}
|
||||
}
|
||||
|
||||
function directiveArgs(
|
||||
directiveUse: NexusDirectiveUse,
|
||||
directiveDef: GraphQLDirective
|
||||
): ReadonlyArray<ArgumentNode> {
|
||||
return mapObj(directiveUse.args ?? {}, (val, key) => {
|
||||
const arg = directiveDef.args.find((a) => a.name === key)
|
||||
if (!arg) {
|
||||
throw new Error(`Unknown directive arg ${key}, expected one of ${directiveDef.args.map((a) => a.name)}`)
|
||||
}
|
||||
const value = astFromValue(val, arg.type)
|
||||
if (!value) {
|
||||
throw new Error(`Unable to get ast for ${key}`)
|
||||
}
|
||||
return {
|
||||
kind: Kind.ARGUMENT,
|
||||
name: {
|
||||
kind: Kind.NAME,
|
||||
value: key,
|
||||
},
|
||||
value,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { assertValidName, GraphQLEnumTypeConfig, GraphQLEnumValueConfig } from 'graphql'
|
||||
import { arg, NexusArgDef, NexusAsArgConfig } from './args'
|
||||
import { NexusTypes, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
import type { Directives } from './directive'
|
||||
import { Maybe, NexusTypes, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
|
||||
type TypeScriptEnumLike = {
|
||||
[key: number]: string
|
||||
|
|
@ -12,29 +13,36 @@ export interface EnumMemberInfo {
|
|||
/** The internal representation of the enum */
|
||||
value?: string | number | object | boolean
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
/**
|
||||
* Info about a field deprecation. Formatted as a string and provided with the deprecated directive on
|
||||
* field/enum types and as a comment on input fields.
|
||||
*/
|
||||
deprecation?: string // | DeprecationInfo;
|
||||
deprecation?: Maybe<string> // | DeprecationInfo;
|
||||
/**
|
||||
* Custom extensions, as supported in graphql-js
|
||||
*
|
||||
* @see https://github.com/graphql/graphql-js/issues/1527
|
||||
*/
|
||||
extensions?: GraphQLEnumValueConfig['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the enum member definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
}
|
||||
|
||||
export interface NexusEnumTypeConfig<TypeName extends string> {
|
||||
name: TypeName
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string | null
|
||||
description?: Maybe<string>
|
||||
/** Source type information for this type */
|
||||
sourceType?: SourceTypingDef
|
||||
/** All members of the enum, either as an array of strings/definition objects, as an object, or as a TypeScript enum */
|
||||
members:
|
||||
| Array<string | EnumMemberInfo>
|
||||
| ReadonlyArray<string | EnumMemberInfo>
|
||||
| Record<string, string | number | object | boolean>
|
||||
| TypeScriptEnumLike
|
||||
/**
|
||||
|
|
@ -43,6 +51,15 @@ export interface NexusEnumTypeConfig<TypeName extends string> {
|
|||
* @see https://github.com/graphql/graphql-js/issues/1527
|
||||
*/
|
||||
extensions?: GraphQLEnumTypeConfig['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the enum type definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
/** Adds this type as a method on the Object/Interface definition blocks */
|
||||
asNexusMethod?: string
|
||||
}
|
||||
|
||||
export class NexusEnumTypeDef<TypeName extends string> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { assertValidName } from 'graphql'
|
||||
import { GetGen } from '../typegenTypeHelpers'
|
||||
import { InputDefinitionBlock } from './definitionBlocks'
|
||||
import type { GetGen } from '../typegenTypeHelpers'
|
||||
import type { InputDefinitionBlock } from './definitionBlocks'
|
||||
import { NexusTypes, withNexusSymbol } from './_types'
|
||||
|
||||
export interface NexusExtendInputTypeConfig<TypeName extends string> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { assertValidName } from 'graphql'
|
||||
import { AllOutputTypesPossible } from '../typegenTypeHelpers'
|
||||
import { ObjectDefinitionBlock } from './objectType'
|
||||
import { IsSubscriptionType, SubscriptionBuilder } from './subscriptionType'
|
||||
import type { AllOutputTypesPossible } from '../typegenTypeHelpers'
|
||||
import type { ObjectDefinitionBlock } from './objectType'
|
||||
import type { IsSubscriptionType, SubscriptionBuilder } from './subscriptionType'
|
||||
import { NexusTypes, withNexusSymbol } from './_types'
|
||||
|
||||
export interface NexusExtendTypeConfig<TypeName extends string> {
|
||||
|
|
@ -21,8 +21,8 @@ export interface NexusExtendTypeConfig<TypeName extends string> {
|
|||
* "definition" method.
|
||||
*
|
||||
* @param t The type builder. Usually the same as that passed to objectType "definition" method except if
|
||||
* extending the Subscription type in which case you get a subscription type builder (which differs
|
||||
* slightly in that it requires implementation of a "subscribe" method on field configurations).
|
||||
* extending the Subscription type in which case you get a subscription type builder (which differs
|
||||
* slightly in that it requires implementation of a "subscribe" method on field configurations).
|
||||
*/
|
||||
definition(
|
||||
t: IsSubscriptionType<TypeName> extends true ? SubscriptionBuilder : ObjectDefinitionBlock<TypeName>
|
||||
|
|
@ -57,10 +57,10 @@ withNexusSymbol(NexusExtendTypeDef, NexusTypes.ExtendObject)
|
|||
* use-case is so common Nexus ships dedicated functions for it: queryField, mutationField, subscriptionField.
|
||||
*
|
||||
* You can extend types before defining them strictly with objectType or the root field functions (queryType
|
||||
* etc.). The typing for "type" property will appear to suggest that you cannot, however once Nexus
|
||||
* reflection has run you'll see that the type you "extended" exists in the schema and that your static
|
||||
* typing error has been resolved. This behaviour is a convenience especially when extending root types which
|
||||
* you might never define in your schema directly.
|
||||
* etc.). The typing for "type" property will appear to suggest that you cannot, however once Nexus reflection
|
||||
* has run you'll see that the type you "extended" exists in the schema and that your static typing error has
|
||||
* been resolved. This behaviour is a convenience especially when extending root types which you might never
|
||||
* define in your schema directly.
|
||||
*
|
||||
* @example
|
||||
* // types/User.ts
|
||||
|
|
@ -85,8 +85,8 @@ withNexusSymbol(NexusExtendTypeDef, NexusTypes.ExtendObject)
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config The specification of which type to extend and how. This is basically a subset of the configuration
|
||||
* object passed to the objectType function.
|
||||
* @param config The specification of which type to extend and how. This is basically a subset of the
|
||||
* configuration object passed to the objectType function.
|
||||
*/
|
||||
export function extendType<TypeName extends AllOutputTypesPossible>(config: NexusExtendTypeConfig<TypeName>) {
|
||||
return new NexusExtendTypeDef(config.type, { ...config, name: config.type }) as NexusExtendTypeDef<any>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { assertValidName, GraphQLInputObjectTypeConfig } from 'graphql'
|
||||
import { arg, NexusArgDef, NexusAsArgConfig } from './args'
|
||||
import { InputDefinitionBlock } from './definitionBlocks'
|
||||
import { NexusTypes, NonNullConfig, withNexusSymbol } from './_types'
|
||||
import type { InputDefinitionBlock } from './definitionBlocks'
|
||||
import type { Directives } from './directive'
|
||||
import { Maybe, NexusTypes, NonNullConfig, withNexusSymbol } from './_types'
|
||||
|
||||
export type NexusInputObjectTypeConfig<TypeName extends string> = {
|
||||
/** Name of the input object type */
|
||||
|
|
@ -9,7 +10,7 @@ export type NexusInputObjectTypeConfig<TypeName extends string> = {
|
|||
/** Definition block for the input type */
|
||||
definition(t: InputDefinitionBlock<TypeName>): void
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
/**
|
||||
* Configures the nullability for the type, check the documentation's "Getting Started" section to learn
|
||||
* more about GraphQL Nexus's assumptions and configuration on nullability.
|
||||
|
|
@ -21,6 +22,15 @@ export type NexusInputObjectTypeConfig<TypeName extends string> = {
|
|||
* @see https://github.com/graphql/graphql-js/issues/1527
|
||||
*/
|
||||
extensions?: GraphQLInputObjectTypeConfig['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the input object type definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
/** Adds this type as a method on the Object/Interface definition blocks */
|
||||
asNexusMethod?: string
|
||||
} & NexusGenPluginInputTypeConfig<TypeName>
|
||||
|
||||
export class NexusInputObjectTypeDef<TypeName extends string> {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
import { assertValidName, GraphQLInterfaceTypeConfig } from 'graphql'
|
||||
import { FieldResolver, GetGen, InterfaceFieldsFor, ModificationType } from '../typegenTypeHelpers'
|
||||
import { ArgsRecord } from './args'
|
||||
import type { FieldResolver, GetGen, InterfaceFieldsFor, ModificationType } from '../typegenTypeHelpers'
|
||||
import type { ArgsRecord } from './args'
|
||||
import { OutputDefinitionBlock, OutputDefinitionBuilder } from './definitionBlocks'
|
||||
import { AbstractTypes, NexusTypes, NonNullConfig, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
import type { Directives } from './directive'
|
||||
import { AbstractTypes, Maybe, NexusTypes, NonNullConfig, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
|
||||
export type Implemented = GetGen<'interfaceNames'> | NexusInterfaceTypeDef<any>
|
||||
|
||||
export interface FieldModification<TypeName extends string, FieldName extends string> {
|
||||
type?: ModificationType<TypeName, FieldName>
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
/** The resolve method we should be resolving the field with */
|
||||
resolve?: FieldResolver<TypeName, FieldName>
|
||||
/** You are allowed to add non-required args when modifying a field */
|
||||
args?: ArgsRecord
|
||||
args?: Maybe<ArgsRecord>
|
||||
/**
|
||||
* Custom extensions, as supported in graphql-js
|
||||
*
|
||||
|
|
@ -44,7 +45,7 @@ export type NexusInterfaceTypeConfig<TypeName extends string> = {
|
|||
*/
|
||||
nonNullDefaults?: NonNullConfig
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string | null
|
||||
description?: Maybe<string>
|
||||
/** Source type information for this type */
|
||||
sourceType?: SourceTypingDef
|
||||
/**
|
||||
|
|
@ -53,6 +54,15 @@ export type NexusInterfaceTypeConfig<TypeName extends string> = {
|
|||
* @see https://github.com/graphql/graphql-js/issues/1527
|
||||
*/
|
||||
extensions?: GraphQLInterfaceTypeConfig<any, any>['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the output field definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
/** Adds this type as a method on the Object/Interface definition blocks */
|
||||
asNexusMethod?: string
|
||||
} & AbstractTypes.MaybeTypeDefConfigFieldResolveType<TypeName>
|
||||
|
||||
export interface InterfaceDefinitionBuilder<TypeName extends string> extends OutputDefinitionBuilder {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { isType } from 'graphql'
|
||||
import { AllNamedTypeDefs, isNexusStruct, NexusListableTypes } from './wrapping'
|
||||
import { isNexusMeta } from './nexusMeta'
|
||||
import { isNexusStruct, NexusListableTypes } from './wrapping'
|
||||
import { NexusTypes, withNexusSymbol } from './_types'
|
||||
/** List() */
|
||||
export type NexusListDefConfig<TypeName extends AllNamedTypeDefs> = {
|
||||
type: TypeName
|
||||
}
|
||||
|
||||
export class NexusListDef<TypeName extends NexusListableTypes> {
|
||||
// @ts-ignore
|
||||
|
|
@ -13,7 +10,12 @@ export class NexusListDef<TypeName extends NexusListableTypes> {
|
|||
|
||||
constructor(readonly ofNexusType: TypeName) {
|
||||
/* istanbul ignore if */
|
||||
if (typeof ofNexusType !== 'string' && !isNexusStruct(ofNexusType) && !isType(ofNexusType)) {
|
||||
if (
|
||||
typeof ofNexusType !== 'string' &&
|
||||
!isNexusStruct(ofNexusType) &&
|
||||
!isNexusMeta(ofNexusType) &&
|
||||
!isType(ofNexusType)
|
||||
) {
|
||||
throw new Error('Cannot wrap unknown types in list(). Saw ' + ofNexusType)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { FieldOutConfig, OutputDefinitionBlock } from '../core'
|
||||
import type { FieldOutConfig, OutputDefinitionBlock } from './definitionBlocks'
|
||||
import { extendType, NexusExtendTypeDef } from './extendType'
|
||||
|
||||
export type MutationFieldConfig<FieldName extends string> =
|
||||
|
|
@ -114,9 +114,8 @@ export function mutationField(
|
|||
* // ...
|
||||
* })
|
||||
*
|
||||
* @param name The name of the field on the Mutation type. Names are case‐sensitive and must conform to pattern:
|
||||
*
|
||||
* [_A-Za-z][_0-9A-Za-z]*
|
||||
* @param name The name of the field on the Mutation type. Names are case‐sensitive and must conform to
|
||||
* pattern: `[_A-Za-z][_0-9A-Za-z]*`
|
||||
* @param config The same type of configuration you would pass to t.field("...", config)
|
||||
*/
|
||||
export function mutationField<FieldName extends string>(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { NexusObjectTypeConfig, objectType } from './objectType'
|
|||
*
|
||||
* @example
|
||||
* mutationType({
|
||||
* definitin(t) {
|
||||
* definition(t) {
|
||||
* t.field('signup', {
|
||||
* type: 'User',
|
||||
* args: {
|
||||
|
|
@ -37,7 +37,7 @@ import { NexusObjectTypeConfig, objectType } from './objectType'
|
|||
* })
|
||||
*
|
||||
* @param config Specify your Mutation type's fields, description, and more. See each config property's jsDoc
|
||||
* for more detail.
|
||||
* for more detail.
|
||||
*/
|
||||
export function mutationType(config: Omit<NexusObjectTypeConfig<'Mutation'>, 'name'>) {
|
||||
return objectType({ ...config, name: 'Mutation' })
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
import { ownProp } from '../utils'
|
||||
import type { NexusInterfaceTypeDef } from './interfaceType'
|
||||
import type { NexusObjectTypeDef } from './objectType'
|
||||
import { isNexusStruct, isNexusInterfaceTypeDef, isNexusObjectTypeDef } from './wrapping'
|
||||
|
||||
/** Symbol marking an object as something that can provide Nexus schema definitions */
|
||||
export const NEXUS_TYPE = Symbol.for('@nexus/meta/NEXUS_TYPE')
|
||||
export const NEXUS_BUILD = Symbol.for('@nexus/meta/NEXUS_BUILD')
|
||||
|
||||
type OutType = NexusObjectTypeDef<any> | NexusInterfaceTypeDef<any>
|
||||
|
||||
/** Object representing a single output or interface type */
|
||||
export type NexusMetaTypeProp = {
|
||||
[NEXUS_TYPE]: OutType
|
||||
}
|
||||
|
||||
export type NexusMetaTypeFn = {
|
||||
[NEXUS_TYPE]: () => OutType
|
||||
}
|
||||
|
||||
export type NexusMetaType = NexusMetaTypeProp | NexusMetaTypeFn
|
||||
|
||||
/** Object containing a symbol defining a function that should be fed into the Nexus type construction layer */
|
||||
export type NexusMetaBuild = {
|
||||
[NEXUS_BUILD]: () => any
|
||||
}
|
||||
|
||||
export type NexusMeta = NexusMetaType | NexusMetaBuild
|
||||
|
||||
export function isNexusMetaBuild(obj: any): obj is NexusMetaBuild {
|
||||
return Boolean(obj && typeof ownProp.get(obj, NEXUS_BUILD) === 'function')
|
||||
}
|
||||
|
||||
export function isNexusMetaType(obj: any): obj is NexusMetaType {
|
||||
return isNexusMetaTypeProp(obj) || isNexusMetaTypeFn(obj)
|
||||
}
|
||||
|
||||
export function isNexusMetaTypeProp(obj: any): obj is NexusMetaTypeProp {
|
||||
return Boolean(obj && ownProp.has(obj, NEXUS_TYPE) && isNexusStruct(ownProp.get(obj, NEXUS_TYPE)))
|
||||
}
|
||||
|
||||
export function isNexusMetaTypeFn(obj: any): obj is NexusMetaTypeFn {
|
||||
return Boolean(obj && ownProp.has(obj, NEXUS_TYPE) && typeof ownProp.get(obj, NEXUS_TYPE) === 'function')
|
||||
}
|
||||
|
||||
export function isNexusMeta(obj: any): obj is NexusMetaBuild | NexusMetaTypeFn | NexusMetaType {
|
||||
return isNexusMetaBuild(obj) || isNexusMetaType(obj) || isNexusMetaTypeFn(obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the thunk, replacing it with the type
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
export function resolveNexusMetaType(obj: NexusMetaType): OutType {
|
||||
let value = ownProp.get(obj, NEXUS_TYPE)
|
||||
if (typeof value === 'function') {
|
||||
value = ownProp.set(obj, NEXUS_TYPE, value.call(obj))
|
||||
}
|
||||
if (!isNexusObjectTypeDef(value) && !isNexusInterfaceTypeDef(value)) {
|
||||
throw new Error(`Expected property of NEXUS_TYPE to be an object or interface type`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { isNonNullType, isType } from 'graphql'
|
||||
import { isNexusMeta } from './nexusMeta'
|
||||
import { isNexusNonNullTypeDef, isNexusNullTypeDef, isNexusStruct, NexusNonNullableTypes } from './wrapping'
|
||||
import { NexusTypes, withNexusSymbol } from './_types'
|
||||
|
||||
|
|
@ -8,7 +9,12 @@ export class NexusNonNullDef<TypeName extends NexusNonNullableTypes> {
|
|||
private _isNexusNonNullDef: boolean = true
|
||||
|
||||
constructor(readonly ofNexusType: TypeName) {
|
||||
if (typeof ofNexusType !== 'string' && !isNexusStruct(ofNexusType) && !isType(ofNexusType)) {
|
||||
if (
|
||||
typeof ofNexusType !== 'string' &&
|
||||
!isNexusStruct(ofNexusType) &&
|
||||
!isNexusMeta(ofNexusType) &&
|
||||
!isType(ofNexusType)
|
||||
) {
|
||||
throw new Error('Cannot wrap unknown types in a nonNull(). Saw ' + ofNexusType)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,20 +60,31 @@ withNexusSymbol(NexusNonNullDef, NexusTypes.NonNull)
|
|||
*
|
||||
* @param type The type to wrap in Non-Null. This may be expressed in one of three ways:
|
||||
*
|
||||
* 1. As string literals matching the name of a builtin scalar. E.g.: 'ID', 'String', ...
|
||||
* 1. As string literals matching the name of a builtin scalar. E.g.: 'ID', 'String', ...
|
||||
* 2. As string literals matching the name of another type. E.g.: 'User', 'Location', ... Thanks to [Nexus'
|
||||
* reflection system](https://nxs.li/guides/reflection) this is typesafe and autocompletable. This is
|
||||
* the idiomatic approach in Nexus because it avoids excessive importing and circular references.
|
||||
* 3. As references to other enums or object type definitions. E.g.: User, Location
|
||||
*
|
||||
* 2. As string literals matching the name of another type. E.g.: 'User', 'Location', ... Thanks to
|
||||
* [Nexus' reflection
|
||||
* system](https://nxs.li/guides/reflection) this is typesafe and autocompletable. This is the idiomatic
|
||||
* approach in Nexus because it avoids excessive importing and circular references.
|
||||
*
|
||||
* 3. As references to other enums or object type definitions. E.g.: User, Location
|
||||
*
|
||||
* You may also use other type modifier helpers like list() which in turn accept one of the three
|
||||
* You may also use other type modifier helpers like list() which in turn accept one of the three
|
||||
*/
|
||||
export function nonNull<TypeName extends NexusNonNullableTypes>(type: TypeName) {
|
||||
if (isNexusNonNullTypeDef(type) || isNonNullType(type)) {
|
||||
return type
|
||||
/*
|
||||
Ran into an issue around the generated return type for `nonNull()`,
|
||||
which produces:
|
||||
|
||||
```ts
|
||||
NexusNonNullDef<any> | (TypeName & GraphQLNonNull<any>)
|
||||
```
|
||||
|
||||
This is problematic when you reach a decent amount of types, where you'll
|
||||
hit a `union type that is too complex to represent` error. Removing the
|
||||
right hand side of the clause resolves the issue, and the fact that it's a
|
||||
`GraphQLNonNull` type is irrelevant, so we can just cast it to
|
||||
`NexusNonNullDef<any>` here
|
||||
*/
|
||||
return type as NexusNonNullDef<any>
|
||||
}
|
||||
if (isNexusNullTypeDef(type)) {
|
||||
return new NexusNonNullDef(type.ofNexusType)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { isType } from 'graphql'
|
||||
import { isNexusMeta } from './nexusMeta'
|
||||
import { isNexusNonNullTypeDef, isNexusNullTypeDef, isNexusStruct, NexusNullableTypes } from './wrapping'
|
||||
import { NexusTypes, withNexusSymbol } from './_types'
|
||||
|
||||
|
|
@ -8,7 +9,12 @@ export class NexusNullDef<TypeName extends NexusNullableTypes> {
|
|||
private _isNexusNullDef: boolean = true
|
||||
|
||||
constructor(readonly ofNexusType: TypeName) {
|
||||
if (typeof ofNexusType !== 'string' && !isNexusStruct(ofNexusType) && !isType(ofNexusType)) {
|
||||
if (
|
||||
typeof ofNexusType !== 'string' &&
|
||||
!isNexusStruct(ofNexusType) &&
|
||||
!isNexusMeta(ofNexusType) &&
|
||||
!isType(ofNexusType)
|
||||
) {
|
||||
throw new Error('Cannot wrap unknown types in nullable(). Saw ' + ofNexusType)
|
||||
}
|
||||
}
|
||||
|
|
@ -58,16 +64,13 @@ withNexusSymbol(NexusNullDef, NexusTypes.Null)
|
|||
*
|
||||
* @param type The type to wrap in Non-Null. This may be expressed in one of three ways:
|
||||
*
|
||||
* 1. As string literals matching the name of a builtin scalar. E.g.: 'ID', 'String', ...
|
||||
* 1. As string literals matching the name of a builtin scalar. E.g.: 'ID', 'String', ...
|
||||
* 2. As string literals matching the name of another type. E.g.: 'User', 'Location', ... Thanks to [Nexus'
|
||||
* reflection system](https://nxs.li/guides/reflection) this is typesafe and autocompletable. This is
|
||||
* the idiomatic approach in Nexus because it avoids excessive importing and circular references.
|
||||
* 3. As references to other enums or object type definitions. E.g.: User, Location
|
||||
*
|
||||
* 2. As string literals matching the name of another type. E.g.: 'User', 'Location', ... Thanks to
|
||||
* [Nexus' reflection
|
||||
* system](https://nxs.li/guides/reflection) this is typesafe and autocompletable. This is the idiomatic
|
||||
* approach in Nexus because it avoids excessive importing and circular references.
|
||||
*
|
||||
* 3. As references to other enums or object type definitions. E.g.: User, Location
|
||||
*
|
||||
* You may also use other type modifier helpers like list() which in turn accept one of the three
|
||||
* You may also use other type modifier helpers like list() which in turn accept one of the three
|
||||
*/
|
||||
export function nullable<TypeName extends NexusNullableTypes>(type: TypeName) {
|
||||
if (isNexusNonNullTypeDef(type)) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { assertValidName, GraphQLObjectType } from 'graphql'
|
||||
import { InterfaceFieldsFor } from '../typegenTypeHelpers'
|
||||
import type { InterfaceFieldsFor } from '../typegenTypeHelpers'
|
||||
import { OutputDefinitionBlock, OutputDefinitionBuilder } from './definitionBlocks'
|
||||
import { FieldModification, FieldModificationDef, Implemented } from './interfaceType'
|
||||
import { AbstractTypes, NexusTypes, NonNullConfig, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
import type { Directives } from './directive'
|
||||
import type { FieldModification, FieldModificationDef, Implemented } from './interfaceType'
|
||||
import { AbstractTypes, Maybe, NexusTypes, NonNullConfig, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
|
||||
export interface ObjectDefinitionBuilder extends OutputDefinitionBuilder {
|
||||
addInterfaces(toAdd: Implemented[]): void
|
||||
|
|
@ -102,7 +103,7 @@ export type NexusObjectTypeConfig<TypeName extends string> = {
|
|||
* // # ...
|
||||
* // }
|
||||
*/
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
/**
|
||||
* [Source Types Guide](https://nxs.li/guides/backing-types)
|
||||
*
|
||||
|
|
@ -118,8 +119,8 @@ export type NexusObjectTypeConfig<TypeName extends string> = {
|
|||
*
|
||||
* @example
|
||||
* {
|
||||
* module: 'some-package',
|
||||
* export: 'User',
|
||||
* "module": "some-package",
|
||||
* "export": "User"
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
|
|
@ -165,10 +166,9 @@ export type NexusObjectTypeConfig<TypeName extends string> = {
|
|||
* within. You can leverage conditionals, loops, other functions (that take the builder api as an
|
||||
* argument), pull in variables from higher scopes, and so on, to help define your fields. However avoid two things:
|
||||
*
|
||||
* 1. Doing asynchronous work when defining fields.
|
||||
*
|
||||
* 2. Triggering side-effects that you would NOT want run at _build_ time––as this code will run during
|
||||
* build to support [Nexus' reflection system](https://nxs.li/guides/reflection).
|
||||
* 1. Doing asynchronous work when defining fields. 2. Triggering side-effects that you would NOT want run
|
||||
* at *build* time––as this code will run during build
|
||||
* to support [Nexus' reflection system](https://nxs.li/guides/reflection).
|
||||
*
|
||||
* @example
|
||||
* objectType({
|
||||
|
|
@ -186,10 +186,19 @@ export type NexusObjectTypeConfig<TypeName extends string> = {
|
|||
* })
|
||||
*
|
||||
* @param t The type builder API for object types. The primary method you'll find is "t.field" but there are
|
||||
* many convenient shorthands available as well, plus anything plugins have added. Explore each one's jsDoc
|
||||
* for more detail.
|
||||
* many convenient shorthands available as well, plus anything plugins have added. Explore each one's
|
||||
* jsDoc for more detail.
|
||||
*/
|
||||
definition(t: ObjectDefinitionBlock<TypeName>): void
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the object type definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
/** Adds this type as a method on the Object/Interface definition blocks */
|
||||
asNexusMethod?: string
|
||||
} & AbstractTypes.MaybeTypeDefConfigFieldIsTypeOf<TypeName> &
|
||||
NexusGenPluginTypeConfig<TypeName>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { FieldOutConfig, OutputDefinitionBlock } from '../core'
|
||||
import type { FieldOutConfig, OutputDefinitionBlock } from './definitionBlocks'
|
||||
import { extendType, NexusExtendTypeDef } from './extendType'
|
||||
|
||||
export type QueryFieldConfig<FieldName extends string> =
|
||||
|
|
@ -101,8 +101,7 @@ export function queryField(
|
|||
* })
|
||||
*
|
||||
* @param name The name of the field on the Query type. Names are case‐sensitive and must conform to pattern:
|
||||
*
|
||||
* [_A-Za-z][_0-9A-Za-z]*
|
||||
* `[_A-Za-z][_0-9A-Za-z]*`
|
||||
* @param config The same type of configuration you would pass to t.field("...", config)
|
||||
*/
|
||||
export function queryField<FieldName extends string>(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { NexusObjectTypeConfig, objectType } from './objectType'
|
|||
*
|
||||
* @example
|
||||
* queryType({
|
||||
* definitin(t) {
|
||||
* definition(t) {
|
||||
* t.field('user', {
|
||||
* type: 'User',
|
||||
* args: {
|
||||
|
|
@ -36,8 +36,8 @@ import { NexusObjectTypeConfig, objectType } from './objectType'
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Specify your Query type's fields, description, and more. See each config property's jsDoc
|
||||
* for more detail.
|
||||
* @param config Specify your Query type's fields, description, and more. See each config property's jsDoc for
|
||||
* more detail.
|
||||
*/
|
||||
export function queryType(config: Omit<NexusObjectTypeConfig<'Query'>, 'name'>) {
|
||||
return objectType({ ...config, name: 'Query' })
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { assertValidName, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'
|
||||
import { assertValidName, GraphQLNamedType, GraphQLScalarTypeConfig } from 'graphql'
|
||||
import type { AllNexusInputTypeDefs, AllNexusOutputTypeDefs } from '../core'
|
||||
import { decorateType } from './decorateType'
|
||||
import { NexusTypes, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
import type { Directives } from './directive'
|
||||
import { GraphQLNamedOutputType, Maybe, NexusTypes, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
|
||||
export interface ScalarBase
|
||||
extends Pick<
|
||||
|
|
@ -10,7 +12,7 @@ export interface ScalarBase
|
|||
|
||||
export interface ScalarConfig {
|
||||
/** Any deprecation info for this scalar type */
|
||||
deprecation?: string // | DeprecationInfo;
|
||||
deprecation?: Maybe<string> // | DeprecationInfo;
|
||||
/** Adds this type as a method on the Object/Interface definition blocks */
|
||||
asNexusMethod?: string
|
||||
/** Source type information for this type */
|
||||
|
|
@ -21,6 +23,13 @@ export interface ScalarConfig {
|
|||
* @see https://github.com/graphql/graphql-js/issues/1527
|
||||
*/
|
||||
extensions?: GraphQLScalarTypeConfig<any, any>['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the scalar type definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
}
|
||||
|
||||
export interface NexusScalarTypeConfig<T extends string> extends ScalarBase, ScalarConfig {
|
||||
|
|
@ -43,13 +52,13 @@ export function scalarType<TypeName extends string>(options: NexusScalarTypeConf
|
|||
return new NexusScalarTypeDef(options.name, options)
|
||||
}
|
||||
|
||||
export function asNexusMethod<T extends GraphQLScalarType>(
|
||||
scalar: T,
|
||||
export function asNexusMethod<T extends GraphQLNamedType>(
|
||||
namedType: T,
|
||||
methodName: string,
|
||||
sourceType?: SourceTypingDef
|
||||
): T {
|
||||
return decorateType(scalar, {
|
||||
): T extends GraphQLNamedOutputType ? AllNexusOutputTypeDefs : AllNexusInputTypeDefs {
|
||||
return decorateType(namedType, {
|
||||
asNexusMethod: methodName,
|
||||
sourceType,
|
||||
})
|
||||
}) as any
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { extendType, NexusExtendTypeDef } from './extendType'
|
||||
import { SubscriptionBuilder, SubscriptionTypeConfig } from './subscriptionType'
|
||||
import type { SubscriptionBuilder, SubscriptionTypeConfig } from './subscriptionType'
|
||||
|
||||
export type SubscriptionFieldConfig<FieldName extends string, Event> =
|
||||
| SubscriptionTypeConfig<FieldName, Event>
|
||||
|
|
@ -15,16 +15,16 @@ export type SubscriptionFieldConfig<FieldName extends string, Event> =
|
|||
* `extendType({ type: 'Subscription' })`
|
||||
*
|
||||
* The Subscription type is one of three [root
|
||||
* types](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) in GraphQL and its fields represent
|
||||
* API operations your clients can run to be pushed data changes over time.
|
||||
* types](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) in GraphQL and its fields represent API
|
||||
* operations your clients can run to be pushed data changes over time.
|
||||
*
|
||||
* Use this instead of subscriptionType if you are going to modularize your schema and thus be wanting to
|
||||
* contribute fields to the Subscription type from multiple modules. You do not have to have previously
|
||||
* defined a Query type before using this. If you haven't Nexus will create one automatically for you.
|
||||
*
|
||||
* Note that the main difference about Subscription type from other object types is that its field
|
||||
* configurations require a special "subscribe" method where you can return an asynchronous iterator.
|
||||
* Promises yielded by that iterator become available to the resolver in its first param, the source data.
|
||||
* configurations require a special "subscribe" method where you can return an asynchronous iterator. Promises
|
||||
* yielded by that iterator become available to the resolver in its first param, the source data.
|
||||
*
|
||||
* If you need to leverage plugins or define multiple fields then use the typeBuilder overload variant of this
|
||||
* function. Otherwise you may prefer to the field name/config variant.
|
||||
|
|
@ -126,16 +126,16 @@ export function subscriptionField(
|
|||
* `extendType({ type: 'Subscription' })`
|
||||
*
|
||||
* The Subscription type is one of three [root
|
||||
* types](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) in GraphQL and its fields represent
|
||||
* API operations your clients can run to be pushed data changes over time.
|
||||
* types](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) in GraphQL and its fields represent API
|
||||
* operations your clients can run to be pushed data changes over time.
|
||||
*
|
||||
* Use this instead of subscriptionType if you are going to modularize your schema and thus be wanting to
|
||||
* contribute fields to the Subscription type from multiple modules. You do not have to have previously
|
||||
* defined a Query type before using this. If you haven't Nexus will create one automatically for you.
|
||||
*
|
||||
* Note that the main difference about Subscription type from other object types is that its field
|
||||
* configurations require a special "subscribe" method where you can return an asynchronous iterator.
|
||||
* Promises yielded by that iterator become available to the resolver in its first param, the source data.
|
||||
* configurations require a special "subscribe" method where you can return an asynchronous iterator. Promises
|
||||
* yielded by that iterator become available to the resolver in its first param, the source data.
|
||||
*
|
||||
* If you need to leverage plugins or define multiple fields then use the typeBuilder overload variant of this
|
||||
* function. Otherwise you may prefer to the field name/config variant.
|
||||
|
|
@ -222,8 +222,7 @@ export function subscriptionField(
|
|||
* })
|
||||
*
|
||||
* @param name The name of the field on the Query type. Names are case‐sensitive and must conform to pattern:
|
||||
*
|
||||
* [_A-Za-z][_0-9A-Za-z]*
|
||||
* `[_A-Za-z][_0-9A-Za-z]*`
|
||||
* @param config The same type of configuration you would pass to t.field("...", config)
|
||||
*/
|
||||
export function subscriptionField<FieldName extends string, Event>(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { GraphQLResolveInfo } from 'graphql'
|
||||
import { ArgsValue, GetGen, MaybePromise, MaybePromiseDeep, ResultValue } from '../typegenTypeHelpers'
|
||||
import { IsEqual } from '../typeHelpersInternal'
|
||||
import { CommonOutputFieldConfig } from './definitionBlocks'
|
||||
import type { GraphQLResolveInfo } from 'graphql'
|
||||
import type { ArgsValue, GetGen, MaybePromise, MaybePromiseDeep, ResultValue } from '../typegenTypeHelpers'
|
||||
import type { IsEqual } from '../typeHelpersInternal'
|
||||
import type { CommonOutputFieldConfig } from './definitionBlocks'
|
||||
import { objectType } from './objectType'
|
||||
import { AllNexusOutputTypeDefs } from './wrapping'
|
||||
import type { AllNexusOutputTypeDefs } from './wrapping'
|
||||
|
||||
export type IsSubscriptionType<T> = IsEqual<T, 'Subscription'>
|
||||
|
||||
|
|
@ -30,11 +30,17 @@ export type SubscriptionScalarConfig<FieldName extends string, Event> =
|
|||
& SubscriptionTypeConfigBase<FieldName, Event>
|
||||
|
||||
// prettier-ignore
|
||||
export interface SubscriptionTypeConfig<FieldName extends string, Event>
|
||||
extends SubscriptionScalarConfig<FieldName, Event>
|
||||
{
|
||||
type: GetGen<'allOutputTypes'> | AllNexusOutputTypeDefs
|
||||
}
|
||||
export interface SubscriptionTypeConfig<FieldName extends string, Event> extends SubscriptionScalarConfig<FieldName, Event> {
|
||||
type: GetGen<'allOutputTypes'> | AllNexusOutputTypeDefs
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export interface SubscriptionTypeConfigWithName<FieldName extends string, Event> extends SubscriptionTypeConfig<FieldName, Event> {
|
||||
/**
|
||||
* The name of this field. Must conform to the regex pattern: [_A-Za-z][_0-9A-Za-z]*
|
||||
*/
|
||||
name: FieldName
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export interface SubscriptionBuilder {
|
||||
|
|
@ -46,7 +52,8 @@ export interface SubscriptionBuilder {
|
|||
boolean<FieldName extends string, Event>(fieldName: FieldName, opts: SubscriptionScalarConfig<FieldName, Event>): void
|
||||
id<FieldName extends string, Event>(fieldName: FieldName, config: SubscriptionScalarConfig<FieldName, Event>): void
|
||||
float<FieldName extends string, Event>(fieldName: FieldName, config: SubscriptionScalarConfig<FieldName, Event>): void
|
||||
field<FieldName extends string, Event>(name: FieldName, fieldConfig: SubscriptionTypeConfig<FieldName, Event>): void
|
||||
field<FieldName extends string, Event>(config: SubscriptionTypeConfigWithName<FieldName, Event>): void
|
||||
field<FieldName extends string, Event>(name: FieldName, config: SubscriptionTypeConfig<FieldName, Event>): void
|
||||
}
|
||||
|
||||
export type SubscriptionTypeParams = {
|
||||
|
|
@ -63,16 +70,16 @@ export type SubscriptionTypeParams = {
|
|||
* `objectType({ name: 'Subscription' })`
|
||||
*
|
||||
* The Subscription type is one of three [root
|
||||
* types](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) in GraphQL and its fields represent
|
||||
* API operations your clients can run to be pushed data changes over time.
|
||||
* types](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) in GraphQL and its fields represent API
|
||||
* operations your clients can run to be pushed data changes over time.
|
||||
*
|
||||
* You can only have one of these in your schema. If you are going to modularize your schema and thus be
|
||||
* wanting to contribute fields to the Subscription type from multiple modules then use
|
||||
* [queryField](https://nxs.li/docs/api/subscription-field) intead.
|
||||
*
|
||||
* Note that the main difference about Subscription type from other object types is that its field
|
||||
* configurations require a special "subscribe" method where you can return an asynchronous iterator.
|
||||
* Promises yielded by that iterator become available to the resolver in its first param, the source data.
|
||||
* configurations require a special "subscribe" method where you can return an asynchronous iterator. Promises
|
||||
* yielded by that iterator become available to the resolver in its first param, the source data.
|
||||
*
|
||||
* @example
|
||||
* // Contrived but simple self-contained example
|
||||
|
|
@ -165,8 +172,8 @@ export type SubscriptionTypeParams = {
|
|||
* },
|
||||
* })
|
||||
*
|
||||
* @param config Specify your Subscription type's fields, description, and more. See each config property's jsDoc
|
||||
* for more detail.
|
||||
* @param config Specify your Subscription type's fields, description, and more. See each config property's
|
||||
* jsDoc for more detail.
|
||||
*/
|
||||
export function subscriptionType(config: SubscriptionTypeParams) {
|
||||
return objectType({ ...config, name: 'Subscription' } as any)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { assertValidName, GraphQLUnionTypeConfig } from 'graphql'
|
||||
import { GetGen } from '../typegenTypeHelpers'
|
||||
import { NexusObjectTypeDef } from './objectType'
|
||||
import { AbstractTypes, NexusTypes, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
import type { Directives } from '../core'
|
||||
import type { GetGen } from '../typegenTypeHelpers'
|
||||
import type { NexusObjectTypeDef } from './objectType'
|
||||
import { AbstractTypes, Maybe, NexusTypes, SourceTypingDef, withNexusSymbol } from './_types'
|
||||
|
||||
export interface UnionDefinitionBuilder {
|
||||
typeName: string
|
||||
|
|
@ -27,12 +28,12 @@ export type NexusUnionTypeConfig<TypeName extends string> = {
|
|||
/** Builds the definition for the union */
|
||||
definition(t: UnionDefinitionBlock): void
|
||||
/** The description to annotate the GraphQL SDL */
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
/**
|
||||
* Info about a field deprecation. Formatted as a string and provided with the deprecated directive on
|
||||
* field/enum types and as a comment on input fields.
|
||||
*/
|
||||
deprecation?: string // | DeprecationInfo;
|
||||
deprecation?: Maybe<string> // | DeprecationInfo;
|
||||
/** Source type information for this type */
|
||||
sourceType?: SourceTypingDef
|
||||
/**
|
||||
|
|
@ -41,6 +42,15 @@ export type NexusUnionTypeConfig<TypeName extends string> = {
|
|||
* @see https://github.com/graphql/graphql-js/issues/1527
|
||||
*/
|
||||
extensions?: GraphQLUnionTypeConfig<any, any>['extensions']
|
||||
/**
|
||||
* A list of directives / directive uses (with args) for the union type definition
|
||||
*
|
||||
* @example
|
||||
* directives: [addDirective('ExampleDirective', { arg: true })]
|
||||
*/
|
||||
directives?: Directives
|
||||
/** Adds this type as a method on the Object/Interface definition blocks */
|
||||
asNexusMethod?: string
|
||||
} & AbstractTypes.MaybeTypeDefConfigFieldResolveType<TypeName>
|
||||
|
||||
export class NexusUnionTypeDef<TypeName extends string> {
|
||||
|
|
|
|||
|
|
@ -8,39 +8,49 @@ import {
|
|||
isListType,
|
||||
isNonNullType,
|
||||
} from 'graphql'
|
||||
import { DynamicInputMethodDef, DynamicOutputMethodDef } from '../dynamicMethod'
|
||||
import { DynamicOutputPropertyDef } from '../dynamicProperty'
|
||||
import { NexusPlugin } from '../plugin'
|
||||
import { AllInputTypes, GetGen } from '../typegenTypeHelpers'
|
||||
import type { DynamicInputMethodDef, DynamicOutputMethodDef } from '../dynamicMethod'
|
||||
import type { DynamicOutputPropertyDef } from '../dynamicProperty'
|
||||
import type { NexusPlugin } from '../plugin'
|
||||
import type { AllInputTypes, GetGen } from '../typegenTypeHelpers'
|
||||
import { PrintedGenTyping, PrintedGenTypingImport, Unreachable } from '../utils'
|
||||
import { NexusArgDef, arg } from './args'
|
||||
import { NexusEnumTypeDef } from './enumType'
|
||||
import { NexusExtendInputTypeDef } from './extendInputType'
|
||||
import { NexusExtendTypeDef } from './extendType'
|
||||
import { NexusInputObjectTypeDef } from './inputObjectType'
|
||||
import { NexusInterfaceTypeDef } from './interfaceType'
|
||||
import type { NexusEnumTypeDef } from './enumType'
|
||||
import type { NexusExtendInputTypeDef } from './extendInputType'
|
||||
import type { NexusExtendTypeDef } from './extendType'
|
||||
import type { NexusInputObjectTypeDef } from './inputObjectType'
|
||||
import type { NexusInterfaceTypeDef } from './interfaceType'
|
||||
import { list, NexusListDef } from './list'
|
||||
import { NexusNonNullDef, nonNull } from './nonNull'
|
||||
import { NexusNullDef, nullable } from './nullable'
|
||||
import { NexusObjectTypeDef } from './objectType'
|
||||
import { NexusScalarTypeDef } from './scalarType'
|
||||
import { NexusUnionTypeDef } from './unionType'
|
||||
import type { NexusObjectTypeDef } from './objectType'
|
||||
import type { NexusScalarTypeDef } from './scalarType'
|
||||
import { isNexusMetaType, NexusMetaType, resolveNexusMetaType } from './nexusMeta'
|
||||
import type { NexusUnionTypeDef } from './unionType'
|
||||
import { NexusTypes, NexusWrappedSymbol } from './_types'
|
||||
import type { NexusDirectiveDef } from './directive'
|
||||
import type { NexusDirectiveUse } from '../core'
|
||||
|
||||
/** Input(named): Nexus only */
|
||||
export type AllNexusNamedInputTypeDefs<T extends string = any> =
|
||||
| NexusInputObjectTypeDef<T>
|
||||
| NexusEnumTypeDef<T>
|
||||
| NexusScalarTypeDef<T>
|
||||
|
||||
/** Input(named): Nexus + GraphQLInput */
|
||||
export type AllNamedInputTypeDefs<T extends string = any> =
|
||||
| AllNexusNamedInputTypeDefs<T>
|
||||
| Exclude<GraphQLInputType, GraphQLList<any> | GraphQLNonNull<any>>
|
||||
|
||||
/** Input(all): Nexus + GraphQL */
|
||||
export type AllNexusInputTypeDefs<T extends string = any> =
|
||||
| AllNexusNamedInputTypeDefs<T>
|
||||
| AllNamedInputTypeDefs<T>
|
||||
| NexusListDef<any>
|
||||
| NexusNonNullDef<any>
|
||||
| NexusNullDef<any>
|
||||
| GraphQLList<any>
|
||||
| GraphQLNonNull<any>
|
||||
|
||||
/** Output(named): Nexus only */
|
||||
export type AllNexusNamedOutputTypeDefs =
|
||||
| NexusObjectTypeDef<any>
|
||||
| NexusInterfaceTypeDef<any>
|
||||
|
|
@ -48,34 +58,53 @@ export type AllNexusNamedOutputTypeDefs =
|
|||
| NexusEnumTypeDef<any>
|
||||
| NexusScalarTypeDef<any>
|
||||
|
||||
/** Output(all): Nexus only */
|
||||
export type AllNexusOutputTypeDefs =
|
||||
| AllNexusNamedOutputTypeDefs
|
||||
| NexusListDef<any>
|
||||
| NexusNonNullDef<any>
|
||||
| NexusNullDef<any>
|
||||
|
||||
/** Input + output(named): Nexus only */
|
||||
export type AllNexusNamedTypeDefs = AllNexusNamedInputTypeDefs | AllNexusNamedOutputTypeDefs
|
||||
|
||||
/** Input + output(all): Nexus only */
|
||||
export type AllNexusTypeDefs = AllNexusOutputTypeDefs | AllNexusInputTypeDefs
|
||||
|
||||
/** Input + output(all): Nexus only + Name */
|
||||
export type AllNamedTypeDefs = AllNexusNamedTypeDefs | GraphQLNamedType
|
||||
|
||||
/** All inputs to list(...) */
|
||||
export type NexusListableTypes =
|
||||
| GetGen<'allNamedTypes', string>
|
||||
| AllNamedTypeDefs
|
||||
| NexusArgDef<any>
|
||||
| NexusListDef<NexusListableTypes>
|
||||
| NexusNonNullDef<NexusNonNullableTypes>
|
||||
| NexusNullDef<NexusNullableTypes>
|
||||
| GraphQLType
|
||||
| NexusMetaType
|
||||
|
||||
export type NexusNonNullableTypes = AllNamedTypeDefs | NexusListDef<NexusListableTypes> | NexusArgDef<any>
|
||||
/** All inputs to nonNull(...) */
|
||||
export type NexusNonNullableTypes =
|
||||
| GetGen<'allNamedTypes', string>
|
||||
| AllNamedTypeDefs
|
||||
| NexusListDef<NexusListableTypes>
|
||||
| NexusArgDef<any>
|
||||
| NexusMetaType
|
||||
|
||||
export type NexusNullableTypes = AllNamedTypeDefs | NexusListDef<NexusListableTypes> | NexusArgDef<any>
|
||||
|
||||
export type AllNamedTypeDefs = GetGen<'allNamedTypes', string> | AllNexusNamedTypeDefs
|
||||
/** All inputs to nullable(...) */
|
||||
export type NexusNullableTypes =
|
||||
| GetGen<'allNamedTypes', string>
|
||||
| AllNamedTypeDefs
|
||||
| NexusListDef<NexusListableTypes>
|
||||
| NexusArgDef<any>
|
||||
| NexusMetaType
|
||||
|
||||
export type AllNexusNamedArgsDefs<T extends AllInputTypes = AllInputTypes> =
|
||||
| T
|
||||
| NexusArgDef<T>
|
||||
| AllNexusNamedInputTypeDefs<T>
|
||||
| AllNamedInputTypeDefs<T>
|
||||
| GraphQLInputType
|
||||
|
||||
export type AllNexusArgsDefs =
|
||||
|
|
@ -152,6 +181,13 @@ export function isNexusArgDef(obj: any): obj is NexusArgDef<AllInputTypes> {
|
|||
return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Arg
|
||||
}
|
||||
|
||||
export function isNexusNamedOuputTypeDef(obj: any): obj is AllNexusNamedOutputTypeDefs {
|
||||
return isNexusNamedTypeDef(obj) && !isNexusInputObjectTypeDef(obj)
|
||||
}
|
||||
export function isNexusNamedInputTypeDef(obj: any): obj is AllNexusNamedInputTypeDefs {
|
||||
return isNexusNamedTypeDef(obj) && !isNexusObjectTypeDef(obj) && !isNexusInterfaceTypeDef(obj)
|
||||
}
|
||||
|
||||
export function isNexusDynamicOutputProperty<T extends string>(obj: any): obj is DynamicOutputPropertyDef<T> {
|
||||
return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DynamicOutputProperty
|
||||
}
|
||||
|
|
@ -172,12 +208,20 @@ export function isNexusPlugin(obj: any): obj is NexusPlugin {
|
|||
return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Plugin
|
||||
}
|
||||
|
||||
export function isNexusDirective(obj: any): obj is NexusDirectiveDef {
|
||||
return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Directive
|
||||
}
|
||||
export function isNexusDirectiveUse(obj: any): obj is NexusDirectiveUse {
|
||||
return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DirectiveUse
|
||||
}
|
||||
|
||||
export type NexusWrapKind = 'NonNull' | 'Null' | 'List'
|
||||
export type NexusFinalWrapKind = 'NonNull' | 'List'
|
||||
|
||||
export function unwrapGraphQLDef(
|
||||
typeDef: GraphQLType
|
||||
): { namedType: GraphQLNamedType; wrapping: NexusFinalWrapKind[] } {
|
||||
export function unwrapGraphQLDef(typeDef: GraphQLType): {
|
||||
namedType: GraphQLNamedType
|
||||
wrapping: NexusFinalWrapKind[]
|
||||
} {
|
||||
const wrapping: NexusFinalWrapKind[] = []
|
||||
let namedType = typeDef
|
||||
while (isWrappingType(namedType)) {
|
||||
|
|
@ -195,15 +239,17 @@ export function unwrapGraphQLDef(
|
|||
|
||||
/** Unwraps any wrapped Nexus or GraphQL types, turning into a list of wrapping */
|
||||
export function unwrapNexusDef(
|
||||
typeDef: AllNexusTypeDefs | AllNexusArgsDefs | GraphQLType | string
|
||||
typeDef: AllNexusTypeDefs | AllNexusArgsDefs | GraphQLType | NexusMetaType | string
|
||||
): {
|
||||
namedType: AllNexusNamedTypeDefs | AllNexusArgsDefs | GraphQLNamedType | string
|
||||
wrapping: NexusWrapKind[]
|
||||
} {
|
||||
const wrapping: NexusWrapKind[] = []
|
||||
let namedType = typeDef
|
||||
while (isNexusWrappingType(namedType) || isWrappingType(namedType)) {
|
||||
if (isWrappingType(namedType)) {
|
||||
while (isNexusWrappingType(namedType) || isWrappingType(namedType) || isNexusMetaType(namedType)) {
|
||||
if (isNexusMetaType(namedType)) {
|
||||
namedType = resolveNexusMetaType(namedType)
|
||||
} else if (isWrappingType(namedType)) {
|
||||
if (isListType(namedType)) {
|
||||
wrapping.unshift('List')
|
||||
} else if (isNonNullType(namedType)) {
|
||||
|
|
@ -233,10 +279,10 @@ export function rewrapAsGraphQLType(baseType: GraphQLNamedType, wrapping: NexusF
|
|||
let finalType: GraphQLType = baseType
|
||||
wrapping.forEach((wrap) => {
|
||||
if (wrap === 'List') {
|
||||
finalType = GraphQLList(finalType)
|
||||
finalType = new GraphQLList(finalType)
|
||||
} else if (wrap === 'NonNull') {
|
||||
if (!isNonNullType(finalType)) {
|
||||
finalType = GraphQLNonNull(finalType)
|
||||
finalType = new GraphQLNonNull(finalType)
|
||||
}
|
||||
} else {
|
||||
throw new Unreachable(wrap)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { PluginBuilderLens } from './builder'
|
||||
import { InputDefinitionBlock, OutputDefinitionBlock } from './definitions/definitionBlocks'
|
||||
import type { PluginBuilderLens } from './builder'
|
||||
import type { InputDefinitionBlock, OutputDefinitionBlock } from './definitions/definitionBlocks'
|
||||
import { NexusTypes, withNexusSymbol } from './definitions/_types'
|
||||
import { NexusWrapKind } from './definitions/wrapping'
|
||||
import type { NexusWrapKind } from './definitions/wrapping'
|
||||
|
||||
export type OutputFactoryConfig<T> = {
|
||||
/** The name of the type this field is being declared on */
|
||||
|
|
@ -63,9 +63,9 @@ withNexusSymbol(DynamicOutputMethodDef, NexusTypes.DynamicOutputMethod)
|
|||
* Defines a new property on the object definition block for an output type, taking arbitrary input to define
|
||||
* additional types. See the connectionPlugin:
|
||||
*
|
||||
* T.connectionField('posts', { nullable: true, totalCount(root, args, ctx, info) {
|
||||
* return ctx.user.getTotalPostCount(root.id, args) }, nodes(root, args, ctx, info) {
|
||||
* return ctx.user.getPosts(root.id, args) } })
|
||||
* T.connectionField('posts', { nullable: true, totalCount(root, args, ctx, info) { return
|
||||
* ctx.user.getTotalPostCount(root.id, args) }, nodes(root, args, ctx, info) { return
|
||||
* ctx.user.getPosts(root.id, args) } })
|
||||
*/
|
||||
export function dynamicOutputMethod<T extends string>(config: DynamicOutputMethodConfig<T>) {
|
||||
return new DynamicOutputMethodDef(config.name, config)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { SchemaBuilder } from './builder'
|
||||
import { OutputDefinitionBlock } from './definitions/definitionBlocks'
|
||||
import type { SchemaBuilder } from './builder'
|
||||
import type { OutputDefinitionBlock } from './definitions/definitionBlocks'
|
||||
import { NexusTypes, withNexusSymbol } from './definitions/_types'
|
||||
import { BaseExtensionConfig } from './dynamicMethod'
|
||||
import type { BaseExtensionConfig } from './dynamicMethod'
|
||||
|
||||
export type OutputPropertyFactoryConfig<T> = {
|
||||
stage: 'walk' | 'build'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
import { defaultFieldResolver, GraphQLNamedType } from 'graphql'
|
||||
import { DynamicFieldDefs, SchemaConfig } from './builder'
|
||||
import { SourceTypingDef, SourceTypings } from './definitions/_types'
|
||||
import { NexusOutputFieldConfig } from './definitions/definitionBlocks'
|
||||
import { NexusInputObjectTypeConfig } from './definitions/inputObjectType'
|
||||
import { NexusInterfaceTypeConfig } from './definitions/interfaceType'
|
||||
import { NexusObjectTypeConfig } from './definitions/objectType'
|
||||
import type { DynamicFieldDefs, SchemaConfig } from './builder'
|
||||
import type { SourceTypings } from './definitions/_types'
|
||||
import type { NexusOutputFieldConfig } from './definitions/definitionBlocks'
|
||||
import type { NexusInputObjectTypeConfig } from './definitions/inputObjectType'
|
||||
import type { NexusInterfaceTypeConfig } from './definitions/interfaceType'
|
||||
import type { NexusObjectTypeConfig } from './definitions/objectType'
|
||||
import type { Directives, FieldSourceType, NamedFieldSourceType } from './core'
|
||||
|
||||
/** @internal */
|
||||
export function hasNexusExtension(val: any): val is any {
|
||||
return Boolean(val)
|
||||
}
|
||||
|
||||
export function isNexusFieldExtension(val: any): val is NexusFieldExtension {
|
||||
return Boolean(val?._type === 'NexusFieldExtension')
|
||||
}
|
||||
|
||||
export type NexusGraphQLNamedType = GraphQLNamedType & {
|
||||
extensions?: {
|
||||
|
|
@ -18,13 +28,17 @@ export type NexusTypeExtensions = NexusObjectTypeExtension | NexusInterfaceTypeE
|
|||
|
||||
/** Container object living on `fieldDefinition.extensions.nexus` */
|
||||
export class NexusFieldExtension<TypeName extends string = any, FieldName extends string = any> {
|
||||
readonly _type = 'NexusFieldExtension' as const
|
||||
readonly config: Omit<NexusOutputFieldConfig<TypeName, FieldName>, 'resolve'>
|
||||
/** Whether the user has provided a custom "resolve" function, or whether we're using GraphQL's defaultResolver */
|
||||
readonly hasDefinedResolver: boolean
|
||||
readonly sourceType: string | FieldSourceType | NamedFieldSourceType[] | undefined
|
||||
|
||||
constructor(config: NexusOutputFieldConfig<TypeName, FieldName>) {
|
||||
const { resolve, ...rest } = config
|
||||
this.config = rest
|
||||
this.hasDefinedResolver = Boolean(resolve && resolve !== defaultFieldResolver)
|
||||
this.sourceType = rest.sourceType
|
||||
}
|
||||
/** Called when there are modifications on the interface fields */
|
||||
modify(modifications: Partial<NexusOutputFieldConfig<any, any>>) {
|
||||
|
|
@ -34,6 +48,7 @@ export class NexusFieldExtension<TypeName extends string = any, FieldName extend
|
|||
|
||||
/** Container object living on `inputObjectType.extensions.nexus` */
|
||||
export class NexusInputObjectTypeExtension<TypeName extends string = any> {
|
||||
readonly _type = 'NexusInputObjectTypeExtension' as const
|
||||
readonly config: Omit<NexusInputObjectTypeConfig<TypeName>, 'definition'>
|
||||
constructor(config: NexusInputObjectTypeConfig<TypeName>) {
|
||||
const { definition, ...rest } = config
|
||||
|
|
@ -43,6 +58,7 @@ export class NexusInputObjectTypeExtension<TypeName extends string = any> {
|
|||
|
||||
/** Container object living on `objectType.extensions.nexus` */
|
||||
export class NexusObjectTypeExtension<TypeName extends string = any> {
|
||||
readonly _type = 'NexusObjectTypeExtension' as const
|
||||
readonly config: Omit<NexusObjectTypeConfig<TypeName>, 'definition' | 'isTypeOf'>
|
||||
constructor(config: NexusObjectTypeConfig<TypeName>) {
|
||||
const { definition, ...rest } = config
|
||||
|
|
@ -52,6 +68,7 @@ export class NexusObjectTypeExtension<TypeName extends string = any> {
|
|||
|
||||
/** Container object living on `interfaceType.extensions.nexus` */
|
||||
export class NexusInterfaceTypeExtension<TypeName extends string = any> {
|
||||
readonly _type = 'NexusInterfaceTypeExtension' as const
|
||||
readonly config: Omit<NexusInterfaceTypeConfig<TypeName>, 'definition' | 'resolveType'>
|
||||
constructor(config: NexusInterfaceTypeConfig<TypeName>) {
|
||||
const { definition, ...rest } = config
|
||||
|
|
@ -61,7 +78,8 @@ export class NexusInterfaceTypeExtension<TypeName extends string = any> {
|
|||
|
||||
export interface NexusSchemaExtensionConfig extends Omit<SchemaConfig, 'types'> {
|
||||
dynamicFields: DynamicFieldDefs
|
||||
rootTypings: SourceTypings
|
||||
sourceTypings: SourceTypings
|
||||
schemaDirectives?: Directives
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -71,10 +89,3 @@ export interface NexusSchemaExtensionConfig extends Omit<SchemaConfig, 'types'>
|
|||
export class NexusSchemaExtension {
|
||||
constructor(readonly config: NexusSchemaExtensionConfig) {}
|
||||
}
|
||||
|
||||
export type NexusScalarExtensions = {
|
||||
nexus: {
|
||||
asNexusMethod?: string
|
||||
sourceType?: SourceTypingDef
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,219 @@
|
|||
const MAX_ARRAY_LENGTH = 10
|
||||
const MAX_RECURSIVE_DEPTH = 2
|
||||
|
||||
/** Used to print values in error messages. */
|
||||
export function inspect(value: unknown): string {
|
||||
return formatValue(value, [])
|
||||
}
|
||||
|
||||
function formatValue(value: unknown, seenValues: ReadonlyArray<unknown>): string {
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return JSON.stringify(value)
|
||||
case 'function':
|
||||
return value.name ? `[function ${value.name}]` : '[function]'
|
||||
case 'object':
|
||||
return formatObjectValue(value, seenValues)
|
||||
default:
|
||||
return String(value)
|
||||
}
|
||||
}
|
||||
|
||||
function formatObjectValue(value: object | null, previouslySeenValues: ReadonlyArray<unknown>): string {
|
||||
if (value === null) {
|
||||
return 'null'
|
||||
}
|
||||
|
||||
if (previouslySeenValues.includes(value)) {
|
||||
return '[Circular]'
|
||||
}
|
||||
|
||||
const seenValues = [...previouslySeenValues, value]
|
||||
|
||||
if (isJSONable(value)) {
|
||||
const jsonValue = value.toJSON()
|
||||
|
||||
// check for infinite recursion
|
||||
if (jsonValue !== value) {
|
||||
return typeof jsonValue === 'string' ? jsonValue : formatValue(jsonValue, seenValues)
|
||||
}
|
||||
} else if (Array.isArray(value)) {
|
||||
return formatArray(value, seenValues)
|
||||
}
|
||||
|
||||
return formatObject(value, seenValues)
|
||||
}
|
||||
|
||||
function isJSONable(value: any): value is { toJSON: () => unknown } {
|
||||
return typeof value.toJSON === 'function'
|
||||
}
|
||||
|
||||
function formatObject(object: object, seenValues: ReadonlyArray<unknown>): string {
|
||||
const entries = Object.entries(object)
|
||||
if (entries.length === 0) {
|
||||
return '{}'
|
||||
}
|
||||
|
||||
if (seenValues.length > MAX_RECURSIVE_DEPTH) {
|
||||
return '[' + getObjectTag(object) + ']'
|
||||
}
|
||||
|
||||
const properties = entries.map(([key, value]) => key + ': ' + formatValue(value, seenValues))
|
||||
return '{ ' + properties.join(', ') + ' }'
|
||||
}
|
||||
|
||||
function formatArray(array: ReadonlyArray<unknown>, seenValues: ReadonlyArray<unknown>): string {
|
||||
if (array.length === 0) {
|
||||
return '[]'
|
||||
}
|
||||
|
||||
if (seenValues.length > MAX_RECURSIVE_DEPTH) {
|
||||
return '[Array]'
|
||||
}
|
||||
|
||||
const len = Math.min(MAX_ARRAY_LENGTH, array.length)
|
||||
const remaining = array.length - len
|
||||
const items = []
|
||||
|
||||
for (let i = 0; i < len; ++i) {
|
||||
items.push(formatValue(array[i], seenValues))
|
||||
}
|
||||
|
||||
if (remaining === 1) {
|
||||
items.push('... 1 more item')
|
||||
} else if (remaining > 1) {
|
||||
items.push(`... ${remaining} more items`)
|
||||
}
|
||||
|
||||
return '[' + items.join(', ') + ']'
|
||||
}
|
||||
|
||||
function getObjectTag(object: object): string {
|
||||
const tag = Object.prototype.toString
|
||||
.call(object)
|
||||
.replace(/^\[object /, '')
|
||||
.replace(/]$/, '')
|
||||
|
||||
if (tag === 'Object' && typeof object.constructor === 'function') {
|
||||
const name = object.constructor.name
|
||||
if (typeof name === 'string' && name !== '') {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
export function invariant(condition: unknown, message?: string): asserts condition {
|
||||
const booleanCondition = Boolean(condition)
|
||||
// istanbul ignore else (See transformation done in './resources/inlineInvariant.js')
|
||||
if (!booleanCondition) {
|
||||
throw new Error(message != null ? message : 'Unexpected invariant triggered.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces the value of a block string from its parsed raw value, similar to CoffeeScript's block string,
|
||||
* Python's docstring trim or Ruby's strip_heredoc.
|
||||
*
|
||||
* This implements the GraphQL spec's BlockStringValue() static algorithm.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function dedentBlockStringValue(rawString: string): string {
|
||||
// Expand a block string's raw value into independent lines.
|
||||
const lines = rawString.split(/\r\n|[\n\r]/g)
|
||||
|
||||
// Remove common indentation from all lines but first.
|
||||
const commonIndent = getBlockStringIndentation(rawString)
|
||||
|
||||
if (commonIndent !== 0) {
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
lines[i] = lines[i].slice(commonIndent)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading and trailing blank lines.
|
||||
let startLine = 0
|
||||
while (startLine < lines.length && isBlank(lines[startLine])) {
|
||||
++startLine
|
||||
}
|
||||
|
||||
let endLine = lines.length
|
||||
while (endLine > startLine && isBlank(lines[endLine - 1])) {
|
||||
--endLine
|
||||
}
|
||||
|
||||
// Return a string of the lines joined with U+000A.
|
||||
return lines.slice(startLine, endLine).join('\n')
|
||||
}
|
||||
|
||||
function isBlank(str: string): boolean {
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
if (str[i] !== ' ' && str[i] !== '\t') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getBlockStringIndentation(value: string): number {
|
||||
let isFirstLine = true
|
||||
let isEmptyLine = true
|
||||
let indent = 0
|
||||
let commonIndent = null
|
||||
|
||||
for (let i = 0; i < value.length; ++i) {
|
||||
switch (value.charCodeAt(i)) {
|
||||
case 13: // \r
|
||||
if (value.charCodeAt(i + 1) === 10) {
|
||||
++i // skip \r\n as one symbol
|
||||
}
|
||||
// falls through
|
||||
case 10: // \n
|
||||
isFirstLine = false
|
||||
isEmptyLine = true
|
||||
indent = 0
|
||||
break
|
||||
case 9: // \t
|
||||
case 32: // <space>
|
||||
++indent
|
||||
break
|
||||
default:
|
||||
if (isEmptyLine && !isFirstLine && (commonIndent === null || indent < commonIndent)) {
|
||||
commonIndent = indent
|
||||
}
|
||||
isEmptyLine = false
|
||||
}
|
||||
}
|
||||
|
||||
return commonIndent ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a block string in the indented block form by adding a leading and trailing blank line. However, if a
|
||||
* block string starts with whitespace and is a single-line, adding a leading blank line would strip that whitespace.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function printBlockString(value: string, preferMultipleLines: boolean = false): string {
|
||||
const isSingleLine = !value.includes('\n')
|
||||
const hasLeadingSpace = value[0] === ' ' || value[0] === '\t'
|
||||
const hasTrailingQuote = value[value.length - 1] === '"'
|
||||
const hasTrailingSlash = value[value.length - 1] === '\\'
|
||||
const printAsMultipleLines = !isSingleLine || hasTrailingQuote || hasTrailingSlash || preferMultipleLines
|
||||
|
||||
let result = ''
|
||||
// Format a multi-line block quote to account for leading space.
|
||||
if (printAsMultipleLines && !(isSingleLine && hasLeadingSpace)) {
|
||||
result += '\n'
|
||||
}
|
||||
result += value
|
||||
if (printAsMultipleLines) {
|
||||
result += '\n'
|
||||
}
|
||||
|
||||
return '"""' + result.replace(/"""/g, '\\"""') + '"""'
|
||||
}
|
||||
|
|
@ -2,8 +2,9 @@ import * as blocks from './blocks'
|
|||
import * as core from './core'
|
||||
|
||||
// All of the Public API definitions
|
||||
export { makeSchema } from './builder'
|
||||
export { makeSchema } from './makeSchema'
|
||||
export { arg, booleanArg, floatArg, idArg, intArg, stringArg } from './definitions/args'
|
||||
export { directive, addDirective } from './definitions/directive'
|
||||
export { decorateType } from './definitions/decorateType'
|
||||
export { enumType } from './definitions/enumType'
|
||||
export { extendInputType } from './definitions/extendInputType'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,171 @@
|
|||
import { GraphQLNamedType, GraphQLSchema, isObjectType, specifiedDirectives } from 'graphql'
|
||||
import { isNexusObjectTypeDef } from './definitions/wrapping'
|
||||
import {
|
||||
AdditionalGraphQLSchemaConfigOptions,
|
||||
ConfiguredTypegen,
|
||||
MakeSchemaOptions,
|
||||
SchemaBuilder,
|
||||
SchemaConfig,
|
||||
} from './builder'
|
||||
import type { NexusGraphQLSchema } from './definitions/_types'
|
||||
import { TypegenMetadata } from './typegenMetadata'
|
||||
import { resolveTypegenConfig } from './typegenUtils'
|
||||
import { assertNoMissingTypes, objValues, runAbstractTypeRuntimeChecks } from './utils'
|
||||
|
||||
/**
|
||||
* Defines the GraphQL schema, by combining the GraphQL types defined by the GraphQL Nexus layer or any
|
||||
* manually defined GraphQLType objects.
|
||||
*
|
||||
* Requires at least one type be named "Query", which will be used as the root query type.
|
||||
*/
|
||||
export function makeSchema(config: SchemaConfig): NexusGraphQLSchema {
|
||||
const { schema, missingTypes, finalConfig } = makeSchemaInternal(config)
|
||||
const typegenConfig = resolveTypegenConfig(finalConfig)
|
||||
const sdl = typegenConfig.outputs.schema
|
||||
const typegen = typegenConfig.outputs.typegen
|
||||
if (sdl || typegen) {
|
||||
// Generating in the next tick allows us to use the schema
|
||||
// in the optional thunk for the typegen config
|
||||
const typegenPromise = new TypegenMetadata(typegenConfig).generateArtifacts(schema)
|
||||
if (config.shouldExitAfterGenerateArtifacts) {
|
||||
let typegenPath = '(not enabled)'
|
||||
if (typegenConfig.outputs.typegen) {
|
||||
typegenPath = typegenConfig.outputs.typegen.outputPath
|
||||
if (typegenConfig.outputs.typegen.globalsPath) {
|
||||
typegenPath += ` / ${typegenConfig.outputs.typegen.globalsPath}`
|
||||
}
|
||||
}
|
||||
typegenPromise
|
||||
.then(() => {
|
||||
console.log(`Generated Artifacts:
|
||||
TypeScript Types ==> ${typegenPath}
|
||||
GraphQL Schema ==> ${typegenConfig.outputs.schema || '(not enabled)'}`)
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
} else {
|
||||
typegenPromise.catch((e) => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
}
|
||||
assertNoMissingTypes(schema, missingTypes)
|
||||
runAbstractTypeRuntimeChecks(schema, finalConfig.features)
|
||||
return schema
|
||||
}
|
||||
|
||||
/** Like makeSchema except that typegen is always run and waited upon. */
|
||||
export async function generateSchema(config: SchemaConfig): Promise<NexusGraphQLSchema> {
|
||||
const { schema, missingTypes, finalConfig } = makeSchemaInternal(config)
|
||||
const typegenConfig = resolveTypegenConfig(finalConfig)
|
||||
await new TypegenMetadata(typegenConfig).generateArtifacts(schema)
|
||||
assertNoMissingTypes(schema, missingTypes)
|
||||
runAbstractTypeRuntimeChecks(schema, finalConfig.features)
|
||||
return schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly useful for testing, generates the schema and returns the artifacts that would have been otherwise
|
||||
* written to the filesystem.
|
||||
*/
|
||||
generateSchema.withArtifacts = async (
|
||||
config: SchemaConfig,
|
||||
typegen: string | null | ConfiguredTypegen = null
|
||||
): Promise<{
|
||||
schema: NexusGraphQLSchema
|
||||
schemaTypes: string
|
||||
tsTypes: string
|
||||
globalTypes: string | null
|
||||
}> => {
|
||||
const { schema, missingTypes, finalConfig } = makeSchemaInternal(config)
|
||||
const typegenConfig = resolveTypegenConfig(finalConfig)
|
||||
const { schemaTypes, tsTypes, globalTypes } = await new TypegenMetadata(
|
||||
typegenConfig
|
||||
).generateArtifactContents(schema, typegen)
|
||||
assertNoMissingTypes(schema, missingTypes)
|
||||
runAbstractTypeRuntimeChecks(schema, finalConfig.features)
|
||||
return { schema, schemaTypes, tsTypes, globalTypes }
|
||||
}
|
||||
|
||||
/** Builds the schema, we may return more than just the schema from this one day. */
|
||||
export function makeSchemaInternal(config: SchemaConfig) {
|
||||
const builder = new SchemaBuilder(config)
|
||||
builder.addTypes(config.types)
|
||||
builder.addTypes(config.directives)
|
||||
if (config.schemaRoots) {
|
||||
builder.addTypes(config.schemaRoots)
|
||||
}
|
||||
|
||||
function getRootType(rootType: 'query' | 'mutation' | 'subscription', defaultType: string) {
|
||||
const rootTypeVal = config.schemaRoots?.[rootType] ?? defaultType
|
||||
let returnVal: null | GraphQLNamedType = null
|
||||
if (typeof rootTypeVal === 'string') {
|
||||
returnVal = typeMap[rootTypeVal]
|
||||
} else if (rootTypeVal) {
|
||||
if (isNexusObjectTypeDef(rootTypeVal)) {
|
||||
returnVal = typeMap[rootTypeVal.name]
|
||||
} else if (isObjectType(rootTypeVal)) {
|
||||
returnVal = typeMap[rootTypeVal.name]
|
||||
}
|
||||
}
|
||||
if (returnVal && !isObjectType(returnVal)) {
|
||||
throw new Error(`Expected ${rootType} to be a objectType, saw ${returnVal.constructor.name}`)
|
||||
}
|
||||
return returnVal
|
||||
}
|
||||
|
||||
const {
|
||||
finalConfig,
|
||||
typeMap,
|
||||
missingTypes,
|
||||
schemaExtension,
|
||||
onAfterBuildFns,
|
||||
customDirectives,
|
||||
schemaDirectives,
|
||||
} = builder.getFinalTypeMap()
|
||||
|
||||
const schema = new GraphQLSchema({
|
||||
...extractGraphQLSchemaOptions(config),
|
||||
query: getRootType('query', 'Query'),
|
||||
mutation: getRootType('mutation', 'Mutation'),
|
||||
subscription: getRootType('subscription', 'Subscription'),
|
||||
types: objValues(typeMap),
|
||||
extensions: {
|
||||
...config.extensions,
|
||||
nexus: schemaExtension,
|
||||
},
|
||||
directives: [...specifiedDirectives, ...Object.values(customDirectives)],
|
||||
...schemaDirectives,
|
||||
}) as NexusGraphQLSchema
|
||||
|
||||
onAfterBuildFns.forEach((fn) => fn(schema))
|
||||
|
||||
return { schema, missingTypes, finalConfig }
|
||||
}
|
||||
|
||||
type OmittedVals = Partial<{ [K in keyof MakeSchemaOptions]: never }>
|
||||
|
||||
function extractGraphQLSchemaOptions(
|
||||
config: SchemaConfig
|
||||
): Partial<AdditionalGraphQLSchemaConfigOptions & OmittedVals> {
|
||||
const {
|
||||
formatTypegen,
|
||||
nonNullDefaults,
|
||||
mergeSchema,
|
||||
outputs,
|
||||
shouldExitAfterGenerateArtifacts,
|
||||
shouldGenerateArtifacts,
|
||||
schemaRoots,
|
||||
sourceTypes,
|
||||
prettierConfig,
|
||||
plugins,
|
||||
customPrintSchemaFn,
|
||||
features,
|
||||
contextType,
|
||||
...graphqlConfigOpts
|
||||
} = config
|
||||
return graphqlConfigOpts
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
export function nodeImports() {
|
||||
const fs = require('fs') as typeof import('fs')
|
||||
const path = require('path') as typeof import('path')
|
||||
return {
|
||||
fs,
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { GraphQLFieldResolver, GraphQLInterfaceType, GraphQLResolveInfo, GraphQLSchema } from 'graphql'
|
||||
import { PluginBuilderLens, SchemaConfig } from './builder'
|
||||
import type { GraphQLFieldResolver, GraphQLInterfaceType, GraphQLResolveInfo, GraphQLSchema } from 'graphql'
|
||||
import type { PluginBuilderLens, SchemaConfig } from './builder'
|
||||
import {
|
||||
Maybe,
|
||||
NexusGraphQLFieldConfig,
|
||||
|
|
@ -9,13 +9,17 @@ import {
|
|||
Omit,
|
||||
withNexusSymbol,
|
||||
} from './definitions/_types'
|
||||
import { InputDefinitionBlock, NexusOutputFieldDef, NexusInputFieldDef } from './definitions/definitionBlocks'
|
||||
import { NexusInputObjectTypeConfig } from './definitions/inputObjectType'
|
||||
import { NexusObjectTypeConfig, ObjectDefinitionBlock } from './definitions/objectType'
|
||||
import { NexusSchemaExtension } from './extensions'
|
||||
import type {
|
||||
InputDefinitionBlock,
|
||||
NexusOutputFieldDef,
|
||||
NexusInputFieldDef,
|
||||
} from './definitions/definitionBlocks'
|
||||
import type { NexusInputObjectTypeConfig } from './definitions/inputObjectType'
|
||||
import type { NexusObjectTypeConfig, ObjectDefinitionBlock } from './definitions/objectType'
|
||||
import type { NexusSchemaExtension } from './extensions'
|
||||
import { isPromiseLike, PrintedGenTyping, PrintedGenTypingImport, venn } from './utils'
|
||||
import { NexusFinalArgConfig } from './definitions/args'
|
||||
import { UnwrapPromise } from './typeHelpersInternal'
|
||||
import type { NexusFinalArgConfig } from './definitions/args'
|
||||
import type { UnwrapPromise } from './typeHelpersInternal'
|
||||
|
||||
export { PluginBuilderLens }
|
||||
|
||||
|
|
@ -30,7 +34,7 @@ export type CreateFieldResolverInfo<FieldExt = any, TypeExt = any> = {
|
|||
parentTypeConfig: (
|
||||
| Omit<NexusGraphQLObjectTypeConfig, 'fields' | 'extensions'>
|
||||
| (Omit<NexusGraphQLInterfaceTypeConfig, 'fields' | 'extensions'> & {
|
||||
interfaces: GraphQLInterfaceType[]
|
||||
interfaces: readonly GraphQLInterfaceType[]
|
||||
})
|
||||
) & {
|
||||
extensions?: Maybe<{ nexus?: { config: TypeExt } }>
|
||||
|
|
@ -47,7 +51,7 @@ export interface PluginConfig {
|
|||
/** A name for the plugin, useful for errors, etc. */
|
||||
name: string
|
||||
/** A description for the plugin */
|
||||
description?: string
|
||||
description?: Maybe<string>
|
||||
/** Any type definitions we want to add to output field definitions */
|
||||
fieldDefTypes?: StringLike | StringLike[]
|
||||
/** Any type definitions we want to add to input field definitions */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { defaultFieldResolver, GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql'
|
||||
import { ArgsRecord, intArg, stringArg } from '../definitions/args'
|
||||
import { CommonFieldConfig, FieldOutConfig } from '../definitions/definitionBlocks'
|
||||
import type {
|
||||
CommonFieldConfig,
|
||||
FieldOutConfig,
|
||||
FieldOutConfigWithName,
|
||||
} from '../definitions/definitionBlocks'
|
||||
import { NexusNonNullDef, nonNull } from '../definitions/nonNull'
|
||||
import { NexusNullDef, nullable } from '../definitions/nullable'
|
||||
import { ObjectDefinitionBlock, objectType } from '../definitions/objectType'
|
||||
|
|
@ -9,10 +13,10 @@ import {
|
|||
AllNexusOutputTypeDefs,
|
||||
applyNexusWrapping,
|
||||
} from '../definitions/wrapping'
|
||||
import { NonNullConfig } from '../definitions/_types'
|
||||
import type { NonNullConfig } from '../definitions/_types'
|
||||
import { dynamicOutputMethod } from '../dynamicMethod'
|
||||
import { completeValue, plugin } from '../plugin'
|
||||
import {
|
||||
import type {
|
||||
ArgsValue,
|
||||
FieldTypeName,
|
||||
GetGen,
|
||||
|
|
@ -21,7 +25,7 @@ import {
|
|||
ResultValue,
|
||||
SourceValue,
|
||||
} from '../typegenTypeHelpers'
|
||||
import { MaybePromiseLike } from '../typeHelpersInternal'
|
||||
import type { MaybePromiseLike } from '../typeHelpersInternal'
|
||||
import { eachObj, getOwnPackage, isPromiseLike, mapObj, pathToArray, printedGenTypingImport } from '../utils'
|
||||
|
||||
export interface ConnectionPluginConfig {
|
||||
|
|
@ -113,8 +117,8 @@ export interface ConnectionPluginConfig {
|
|||
* Any additional fields to make available to the connection type, beyond edges / pageInfo / nodes.
|
||||
*
|
||||
* Any fields defined extended on the Connection type will automatically receive the args from the
|
||||
* connection. If the field also defines args, they will be merged with the args of the connection, with
|
||||
* the extension's field args taking precedence if there is a conflict.
|
||||
* connection. If the field also defines args, they will be merged with the args of the connection, with the
|
||||
* extension's field args taking precedence if there is a conflict.
|
||||
*/
|
||||
extendConnection?: Record<
|
||||
string,
|
||||
|
|
@ -156,10 +160,9 @@ export interface ConnectionPluginConfig {
|
|||
}
|
||||
|
||||
// Extract the node value from the connection for a given field.
|
||||
export type NodeValue<TypeName extends string = any, FieldName extends string = any> = ResultValue<
|
||||
EdgeTypeLookup<TypeName, FieldName>,
|
||||
'node'
|
||||
>
|
||||
export type NodeValue<TypeName extends string = any, FieldName extends string = any> = SourceValue<
|
||||
EdgeTypeLookup<TypeName, FieldName>
|
||||
>['node']
|
||||
|
||||
export type ConnectionFieldConfig<TypeName extends string = any, FieldName extends string = any> = {
|
||||
type: GetGen<'allOutputTypes', string> | AllNexusNamedOutputTypeDefs
|
||||
|
|
@ -300,8 +303,8 @@ export type ConnectionFieldConfig<TypeName extends string = any, FieldName exten
|
|||
| {
|
||||
/**
|
||||
* Implement the full resolve, including `edges` and `pageInfo`. Useful in more complex pagination
|
||||
* cases, or if you want to use utilities from other libraries like GraphQL Relay JS, and only use
|
||||
* Nexus for the construction and type-safety:
|
||||
* cases, or if you want to use utilities from other libraries like GraphQL Relay JS, and only use Nexus
|
||||
* for the construction and type-safety:
|
||||
*
|
||||
* Https://github.com/graphql/graphql-relay-js
|
||||
*/
|
||||
|
|
@ -491,11 +494,11 @@ export const connectionPlugin = (connectionPluginConfig?: ConnectionPluginConfig
|
|||
definition(t2) {
|
||||
t2.list.field('edges', {
|
||||
type: edgeName as any,
|
||||
description: `https://facebook.github.io/relay/graphql/connections.htm#sec-Edge-Types`,
|
||||
description: `https://relay.dev/graphql/connections.htm#sec-Edge-Types`,
|
||||
})
|
||||
t2.nonNull.field('pageInfo', {
|
||||
type: 'PageInfo' as any,
|
||||
description: `https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo`,
|
||||
description: `https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo`,
|
||||
})
|
||||
if (includeNodesField) {
|
||||
t2.list.field('nodes', {
|
||||
|
|
@ -527,11 +530,11 @@ export const connectionPlugin = (connectionPluginConfig?: ConnectionPluginConfig
|
|||
definition(t2) {
|
||||
t2.field('cursor', {
|
||||
type: cursorType ?? nonNull('String'),
|
||||
description: 'https://facebook.github.io/relay/graphql/connections.htm#sec-Cursor',
|
||||
description: 'https://relay.dev/graphql/connections.htm#sec-Cursor',
|
||||
})
|
||||
t2.field('node', {
|
||||
type: targetType,
|
||||
description: 'https://facebook.github.io/relay/graphql/connections.htm#sec-Node',
|
||||
description: 'https://relay.dev/graphql/connections.htm#sec-Node',
|
||||
})
|
||||
if (pluginExtendEdge) {
|
||||
eachObj(pluginExtendEdge, (val, key) => {
|
||||
|
|
@ -555,7 +558,7 @@ export const connectionPlugin = (connectionPluginConfig?: ConnectionPluginConfig
|
|||
objectType({
|
||||
name: 'PageInfo',
|
||||
description:
|
||||
'PageInfo cursor, as defined in https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo',
|
||||
'PageInfo cursor, as defined in https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo',
|
||||
definition(t2) {
|
||||
t2.nonNull.field('hasNextPage', {
|
||||
type: 'Boolean',
|
||||
|
|
@ -706,10 +709,8 @@ export function makeResolveFn(
|
|||
return (root, args: PaginationArgs, ctx, info) => {
|
||||
const { nodes: nodesResolve } = fieldConfig
|
||||
const { decodeCursor = base64Decode, encodeCursor = base64Encode } = pluginConfig
|
||||
const {
|
||||
pageInfoFromNodes = defaultPageInfoFromNodes,
|
||||
cursorFromNode = defaultCursorFromNode,
|
||||
} = mergedConfig
|
||||
const { pageInfoFromNodes = defaultPageInfoFromNodes, cursorFromNode = defaultCursorFromNode } =
|
||||
mergedConfig
|
||||
if (!nodesResolve) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -723,10 +724,6 @@ export function makeResolveFn(
|
|||
formattedArgs.after = decodeCursor(args.after).replace(CURSOR_PREFIX, '')
|
||||
}
|
||||
|
||||
if (args.last && !args.before && cursorFromNode === defaultCursorFromNode) {
|
||||
throw new Error(`Cannot paginate backward without a "before" cursor by default.`)
|
||||
}
|
||||
|
||||
// Local variable to cache the execution of fetching the nodes,
|
||||
// which is needed for all fields.
|
||||
let cachedNodes: MaybePromiseLike<Array<any>>
|
||||
|
|
@ -803,10 +800,9 @@ export function makeResolveFn(
|
|||
})
|
||||
|
||||
if (hasPromise) {
|
||||
return Promise.all([
|
||||
Promise.all(resolvedEdgeList),
|
||||
Promise.all(resolvedNodeList),
|
||||
]).then(([edges, nodes]) => ({ edges, nodes }))
|
||||
return Promise.all([Promise.all(resolvedEdgeList), Promise.all(resolvedNodeList)]).then(
|
||||
([edges, nodes]) => ({ edges, nodes })
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -927,9 +923,16 @@ function mergeArgs(obj: object, fieldArgs: ArgsValue<any, any>): ArgsValue<any,
|
|||
*/
|
||||
function provideArgs(block: ObjectDefinitionBlock<any>, fn: () => void) {
|
||||
const fieldDef = block.field
|
||||
block.field = function (fieldName, config) {
|
||||
block.field = function (
|
||||
...args:
|
||||
| [name: string, config: FieldOutConfig<any, string>]
|
||||
| [config: FieldOutConfigWithName<any, string>]
|
||||
) {
|
||||
let config = args.length === 2 ? { name: args[0], ...args[1] } : args[0]
|
||||
|
||||
const { resolve = defaultFieldResolver } = config
|
||||
fieldDef.call(this, fieldName, {
|
||||
|
||||
fieldDef.call(this, {
|
||||
...config,
|
||||
resolve(root, args, ctx, info) {
|
||||
return resolve(root, mergeArgs(root, args), ctx, info)
|
||||
|
|
@ -942,9 +945,16 @@ function provideArgs(block: ObjectDefinitionBlock<any>, fn: () => void) {
|
|||
|
||||
function provideSourceAndArgs(block: ObjectDefinitionBlock<any>, fn: () => void) {
|
||||
const fieldDef = block.field
|
||||
block.field = function (fieldName, config) {
|
||||
block.field = function (
|
||||
...args:
|
||||
| [name: string, config: FieldOutConfig<any, string>]
|
||||
| [config: FieldOutConfigWithName<any, string>]
|
||||
) {
|
||||
let config = args.length === 2 ? { name: args[0], ...args[1] } : args[0]
|
||||
|
||||
const { resolve = defaultFieldResolver } = config
|
||||
fieldDef.call(this, fieldName, {
|
||||
|
||||
fieldDef.call(this, {
|
||||
...config,
|
||||
resolve(root, args, ctx, info) {
|
||||
return resolve(root.__connectionSource, mergeArgs(root, args), ctx, info)
|
||||
|
|
@ -958,17 +968,16 @@ function provideSourceAndArgs(block: ObjectDefinitionBlock<any>, fn: () => void)
|
|||
function iterateNodes(nodes: any[], args: PaginationArgs, cb: (node: any, i: number) => void) {
|
||||
// If we want the first N of an array of nodes, it's pretty straightforward.
|
||||
if (typeof args.first === 'number') {
|
||||
for (let i = 0; i < args.first; i++) {
|
||||
if (i < nodes.length) {
|
||||
cb(nodes[i], i)
|
||||
}
|
||||
const len = Math.min(args.first, nodes.length)
|
||||
for (let i = 0; i < len; i++) {
|
||||
cb(nodes[i], i)
|
||||
}
|
||||
} else if (typeof args.last === 'number') {
|
||||
for (let i = 0; i < args.last; i++) {
|
||||
const idx = nodes.length - args.last + i
|
||||
if (idx >= 0) {
|
||||
cb(nodes[idx], i)
|
||||
}
|
||||
const len = Math.min(args.last, nodes.length)
|
||||
const startOffset = Math.max(nodes.length - args.last, 0)
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
cb(nodes[i + startOffset], i)
|
||||
}
|
||||
} else {
|
||||
// Only happens if we have a custom validateArgs that ignores first/last
|
||||
|
|
@ -1037,7 +1046,7 @@ function defaultCursorFromNode(
|
|||
args: PaginationArgs,
|
||||
ctx: any,
|
||||
info: GraphQLResolveInfo,
|
||||
{ index }: { index: number; nodes: any[] }
|
||||
{ index, nodes }: { index: number; nodes: any[] }
|
||||
) {
|
||||
let cursorIndex = index
|
||||
// If we're paginating forward, assume we're incrementing from the offset provided via "after",
|
||||
|
|
@ -1052,13 +1061,9 @@ function defaultCursorFromNode(
|
|||
// If we're paginating backward, assume we're working backward from the assumed length
|
||||
// e.g. [0...20] (last: 5, before: "cursor:20") -> [cursor:15, cursor:16, cursor:17, cursor:18, cursor:19]
|
||||
if (typeof args.last === 'number') {
|
||||
if (args.before) {
|
||||
const offset = parseInt(args.before, 10)
|
||||
cursorIndex = offset - args.last + index
|
||||
} else {
|
||||
/* istanbul ignore next */
|
||||
throw new Error('Unreachable')
|
||||
}
|
||||
const offset = args.before ? parseInt(args.before, 10) : nodes.length
|
||||
const len = Math.min(nodes.length, args.last)
|
||||
cursorIndex = offset - len + index
|
||||
}
|
||||
return `${CURSOR_PREFIX}${cursorIndex}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { NexusFinalArgConfig } from '../definitions/args'
|
||||
import { NexusInputFieldDef, NexusOutputFieldDef } from '../definitions/definitionBlocks'
|
||||
import type { NexusFinalArgConfig } from '../definitions/args'
|
||||
import type { NexusInputFieldDef, NexusOutputFieldDef } from '../definitions/definitionBlocks'
|
||||
import { list } from '../definitions/list'
|
||||
import { nonNull } from '../definitions/nonNull'
|
||||
import { nullable } from '../definitions/nullable'
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue