Compare commits

...

103 Commits

Author SHA1 Message Date
Sam Burba 9e5c27c509
docs: Fix readme converter link (#1108) 2023-03-16 11:04:52 -04:00
Jan Piotrowski 601c2f03f4
fix(prisma): Replace outdated note (#1153)Co-authored-by: Andrew Carlson <5479270+andrewicarlson@users.noreply.github.com>
* fix(prisma): Remove outdated note

* Update 010-overview.mdx

---------

Co-authored-by: Andrew Carlson <5479270+andrewicarlson@users.noreply.github.com>
2023-03-16 11:04:26 -04:00
Andrew Carlson 4e085dc7b2
Feature/update docs (#1136)
* Replaced Prisma reference

* Updated copyright info
2023-02-02 10:48:58 -06:00
Alex Kunin 1e6b9212d3
chore: remove irrelevant paragraph in docs (#1128)
Resolver shorthands were removed in v0.18.0, docs need to be updated.
2022-10-31 09:25:32 -04:00
Tim Griesser 4d8e37177b
feat: better esm support, remove top-level node imports (#1112)
* test for esm/esbuild use of nexus

* refactor: add nodeImports, remove top-level references

* Don't minify the esbuild test script

* A bit more explicit of a check on the process being node
2022-07-02 16:08:16 -04:00
Tim Griesser eec4b91091
feat: add sourceType option to fieldDefinition (#1106)
* feat: add sourceType option to fieldDefinition

* add additional configurability for field-level sourceType
2022-06-28 13:21:58 -04:00
Tim Griesser 8e65081539
fix: remove hasSDLDirectives internal state (#1091)
* fix: remove hasSDLDirectives from schema construction
2022-05-16 00:12:23 -04:00
Tim Griesser 7ee48c4f2c
fix: add missing as const on the RequestDirectiveLocation (#1090) 2022-05-15 22:48:26 -04:00
Tim Griesser e9dc7e0c4a chore: Fix codecov 2022-05-15 16:59:29 -04:00
Tim Griesser 9875e90dae
feat: Add Schema Directives to SDL (#952)
Adds directive & addDirective utilities for defining directives emitted in the SDL
2022-05-15 16:59:05 -04:00
Tim Griesser 9a1050792b chore: attempt to fix the dripip workflow, again 2022-05-15 14:56:32 -04:00
Tim Griesser df9cb700a9 chore: update the dripip workflow, bump lint-staged 2022-05-15 14:50:03 -04:00
Tim Griesser 123dc61fac
fix: incorrect logic in backward pagination (#1084) 2022-05-03 15:35:21 -04:00
Tim Griesser 251af9461b fix: update snapshots for change in #1083 2022-05-03 11:38:57 -04:00
Daniel Schwartz b906288c58
chore: change facebook.github.io to relay.dev links (#1083) 2022-05-03 11:24:45 -04:00
Tim Griesser 11d028277e
feat: allow specifying custom directives in makeSchema (#1065) 2022-03-25 10:38:08 -04:00
Jonathan Zinger 509c246e88
Update comment for `shouldGenerateArtifacts` (#1057)
Update comment to reflect the current behavior as per [`src/typegenUtils.ts`](bc12ca0f8e/src/typegenUtils.ts (L11))
2022-03-25 10:28:24 -04:00
Vincent François aaa45204b6
feat: Run both formatTypegen and prettier formatter if given (#1042) 2022-03-25 08:20:32 -04:00
Tim Griesser 7349e3633c
feat: allow specifying custom directives in makeSchema (#1064)
* feat: allow specifying custom directives in makeSchema

* fix for earlier graphql snapshots
2022-03-24 18:48:41 -04:00
Vincent François 892af670a4
feat: Use ReadonlyArray in typings (#1041)
* Use ReadonlArray when generating output types

* Add useReadonlyArrayForInputs config and use it accordingly

* Update tests snapshots

* Move useReadonlyArrayForInputs in ConfiguredTypegen option

* Add tests for useReadonlyArrayForInputs

Co-authored-by: Jason Kuhrt <jasonkuhrt@me.com>
Co-authored-by: Tim Griesser <tgriesser10@gmail.com>
2022-03-24 17:02:17 -04:00
Jason Kuhrt bc12ca0f8e chore: use dripip reusable workflow 2022-03-10 14:02:20 -05:00
Tim Griesser 0f37c3e9d4
docs: Update npm badge 2022-03-05 15:29:59 -05:00
Tim Griesser 6c1530a3d5 v1.3.0 2022-03-05 15:26:42 -05:00
Tim Griesser 92f20dc9e9
chore: add test confirming v16 schema compat (#1054) 2022-03-05 15:25:11 -05:00
Tim Griesser bbc969a5b0
chore: update github workflows (#1053) 2022-03-05 14:12:48 -05:00
Tim Griesser 0d06f26b12
feat: add GraphQL 16 support (#977) 2022-02-17 12:47:40 -05:00
Aniruddh Mukherjee e901bebd51
chore(docs): fix typos (#1005)
Fixed the typos where `draftToPublish` (used in the previous example) were mistakenly written as `postToPublish` which might lead to confusion for some readers.
2021-12-15 09:10:02 -05:00
Riccardo Scalco 87a82a28b1
chore(docs): fix typo 06-chapter-5-persisting-data-via-prisma.mdx (#1007) 2021-12-15 09:09:34 -05:00
Riccardo Scalco 3de7f399c6
chore(docs): fix typo 04-why-nexus.mdx (#1008) 2021-12-15 09:09:02 -05:00
Tana M Berry 45b9d5a926
chore(docs): fix typo 030-neuxs-framework-prisma-users.mdx (#1016) 2021-12-15 09:08:30 -05:00
ChengQing b92545fe4f
chore(docs): fix typo 05-chapter-4-testing-your-api.mdx (#1023)
Fix typo.
2021-12-15 09:08:03 -05:00
ChengQing 4d035019a4
chore(docs): fix typo in 07-chapter-6-testing-with-prisma.mdx (#1024) 2021-12-15 09:07:24 -05:00
Tim Griesser 9ec3442539
fix: Minimum GraphQL v16 support (#1017) 2021-11-18 15:53:18 -05:00
Johan Kim c0e55b53b4
chore(docs): add Example Code (#948) 2021-10-26 21:50:03 -04:00
Johan Kim a7c26ba587
chore(docs): Update Ordering section (#950) 2021-10-26 21:49:26 -04:00
lockedNLevered 703d5595da
chore(docs): Nexus Getting started: missing type in queryType (#995)
queryType is missing type field. TypeScript would throw error and not compile.
2021-10-26 21:48:29 -04:00
Ella Nan 02015ede84
chore(docs): readme update -- fix code sample to include ".ts" in module path (#998) 2021-10-26 21:47:51 -04:00
Dani Guardiola 2934d6d38c
chore(docs): typo fix in docs (#999) 2021-10-26 21:47:19 -04:00
safaure 5e3b837ec0
chore(docs): change package get-port ver in tutorial (#1000)
See https://github.com/sindresorhus/get-port/releases

get-port does not support commonJS anymore.
2021-10-26 21:46:53 -04:00
Timo Heman b24d132d38
docs: change all "npm add" to "npm install" (#991) 2021-09-17 11:19:52 -04:00
Andrew McCallum e5658cadba
docs: Update 061-list-nonNull.mdx (#986)
Fixed syntax in docs where comma was missing
2021-09-07 09:07:05 -04:00
Tim Griesser 333dfb8757
feat: Add mergeSchema, better graphql-js interop (#983)
- Adds a new feature mergeSchema to the makeSchema config, to consume an external schema and merge with the locally defined Nexus types
- Standardizes the consumption of external GraphQLNamedType's so they are converted into Nexus type definitions, simplifying code paths
- Order of resolution goes from local Nexus types -> graphql-js types -> external schema types
- Adds asNexusMethod to all types, allowing for any commonly used types to be used as methods, not just scalars
- Convert methods / properties from protected -> private in Builder to better detect unused code
2021-09-06 09:32:43 -04:00
Tim Griesser 02a73b5d74
refactor: More internal cleanup (#981)
- Add Maybe on resolveType to match GraphQL signature
- Don't create placeholder Query type when renamed
- Internal rename rootTypings -> sourceTypings
2021-09-03 17:00:35 +00:00
Tim Griesser 7e744aa0d3
refactor: Internal type cleanup (#980)
* refactor: Internal type cleanup
* chore: Remove prettier-plugin-jsdoc as its output is non-determinstic
2021-09-03 14:04:28 +00:00
Tim Griesser e65b5a7952
refactor: remove duplicate core schema type checks (#978) 2021-09-02 23:44:37 +00:00
Tim Griesser d4c6260774
feat: ability to rename root types (#976)
Closes #867
2021-09-02 09:38:16 -04:00
Gary Lamp 6de1cb6917
docs: Remove `.ts` extension from import statement (#974) 2021-09-02 09:20:54 -04:00
Tim Griesser 555ae79ded
fix: revert declareInputs default to false (#975) 2021-09-02 13:18:47 +00:00
Tim Griesser a003ff074f
fix: printer imports, follow up from #967 (#970) 2021-08-19 14:56:35 +00:00
Johannes Choo 855a482c54
fix: plugin inputFieldDefTypes (#919) 2021-08-18 14:50:09 -04:00
Tim Griesser 266f1a82ce
fix: add globalHeaders to imports for global def file (#969) 2021-08-17 20:15:49 -04:00
Tim Griesser 380ed9d4ca
fix: correct imports for changes in #967 (#968)
* fix: correct imports for changes in #967
2021-08-17 19:35:56 -04:00
Tim Griesser 5984ee122e
feat: ConfiguredTypegen for splitting global types & configuring input type emission (#967)
* feat: ConfiguredTypegen for splitting globals from other types
* default the declareInputs to true
2021-08-17 15:49:47 -04:00
Tim Griesser a914cc33d1
Revert "fix: #921 wrong typegen for nullable values w/ default (#965)" (#966)
This reverts commit 03b5ffd24a.
2021-08-17 13:50:36 -04:00
Tim Griesser 03b5ffd24a
fix: #921 wrong typegen for nullable values w/ default (#965)
* fix: #921 wrong typegen for nullable values w/ default

* refactor: make the conditional easier to follow
2021-08-04 10:59:50 -04:00
Tim Griesser 4531519e6c fix failing CI types 2021-07-21 12:19:17 -04:00
Tobias Lins 8e0ce1b4a1
chore(docs): improve scalar documentation (#939) 2021-06-25 13:42:06 -04:00
Jason Kuhrt df7d1370f9
feat: field config with name (#938) 2021-06-23 16:27:02 -04:00
Anton Lukin ebcdb29a3e
chore(docs): Fix a typo in JSDoc example (#937) 2021-06-21 08:20:39 -04:00
Tim Griesser 81ec5c0f08
fix: Allow meta types to be wrapped with null/list (#935) 2021-06-19 13:36:07 -04:00
Tim Griesser 2b0708d675
refactor: TO_NEXUS -> NEXUS_TYPE / NEXUS_BUILD (#934) 2021-06-16 08:42:44 -04:00
Tim Griesser 2759c551c7
feat: TO_NEXUS symbol for adding types via metaprogramming (#932) 2021-06-14 17:37:30 -04:00
Nenad Filipovic 10ea65f6f1
chore(docs): add formatTypegen example (#930) 2021-06-11 17:47:21 -04:00
Tim Griesser 690abb7299
fix: correct edge node typing (#876) 2021-06-10 07:49:34 -04:00
Tim Griesser 2e1c32b794
refactor: move makeSchema into separate file from builder (#927) 2021-06-09 11:40:53 -04:00
Tom Sherman 3a92760058
feat: Accept `ReadonlyArray<T>` for enum members (#916) 2021-05-25 19:36:07 -04:00
Philipp Strathausen 031c77257a
chore(docs): Fix prisma link in examples README (#912) 2021-05-25 19:32:31 -04:00
Tom Sherman 31a6137f90
Fix graphql docs link for interfaces (#913) 2021-05-22 12:53:16 -04:00
Jan Piotrowski 571fa4d124
docs(README): Add link to GH Actions 2021-05-20 22:00:43 +02:00
Jan Piotrowski 145f664914
chore(tests): update snapshots that were forgotten in #887 (#910) 2021-05-20 15:01:42 -04:00
Thilllon 189a5becb7
chore(docs): fix proper variable name (#904)
Change the variable name appropriately in the example code
https://nexusjs.org/docs/api/query-field
2021-05-12 08:23:22 -04:00
ahmedosama7450 70eda57c61
chore(docs): nexus nullability defaults (#900) 2021-04-30 15:41:05 -04:00
AstroAsh 7ff557b6e7
fix: sync should gen artifact logic with what docs say(#884) 2021-04-15 19:37:01 -04:00
Harsh Singh 5844b7fa74
chore(docs): update API link to Prisma object modeling (#890) 2021-04-15 19:35:55 -04:00
Alex Ruheni cfff794306
updated test script and server start (#882) 2021-04-13 22:35:34 -04:00
Nikolay Lanets d2e586bd3f
refactor: make errors more readable (#887) 2021-04-12 10:02:00 -04:00
ahmed osama 7b84cd63db
chore(docs): broken link (#881) 2021-04-08 20:01:32 -04:00
ahmed osama 3c18d6c1e3
chore(docs): Nexus nullability defaults to that everything is null by default 2021-04-08 09:30:18 -04:00
Max Leon 3f05c1d0ea
Resolve bullet indentation issue (#879)
fixes #849
2021-04-07 09:34:10 -04:00
Yash 16ac615f9b
chore(docs) Fix Broken Link in Tutorial Chapter 2 (#877)
Fixes the issue #858
2021-04-02 15:13:12 -04:00
Alex Ruheni 9a3915e11c
chore(docs): updated prisma references (#874) 2021-04-01 08:49:00 -04:00
ZC ce1bd8c594
chore(docs): fix typo in abstract types documentation (#873) 2021-03-28 21:58:25 -04:00
Tim Griesser 74c93e2ce4
refactor: add importsNotUsedAsValues: error to force import type (#872) 2021-03-27 16:26:13 -04:00
Renato Benkendorf a59273de88
docs(content of API to makeSchema): update the example of using a custom typing to GraphQL context value (#871) 2021-03-25 07:51:25 -04:00
Alex Ruheni 5056129b40
chore(docs): removed databaseUrl in final code sample testing (#863) 2021-03-15 21:34:18 -04:00
Nil Portugués Calderó c89c72bda2
chore(docs): Change --transpileOnly to --transpile-only from star-wars example (#859)
* Change --transpileOnly to --transpile-only
2021-03-15 07:40:41 -04:00
Alex Ruheni 3050c3bb42
chore(docs): Updated getting started tutorial (#852)
- migrated from postgres to sqlite
-fixed tests 🤞🏽
2021-03-04 14:25:18 -05:00
Tim Griesser 7f16b8b750
fix: cast NexusNonNullDef to prevent union type complexity error (#846) 2021-03-04 14:24:29 -05:00
Anton Niklasson 291e86a669
chore(docs): fix broken link in 070-make-schema.mdx (#851) 2021-03-03 12:58:48 -05:00
Anton Niklasson 4625a13b52
chore(docs): update 010-object-type.mdx (#847) 2021-03-02 21:55:44 -05:00
Nikolas 8827f28ead
chore(docs): Update 06-chapter-5-persisting-data-via-prisma.mdx (#848) 2021-03-02 21:55:10 -05:00
ahmed osama d220d3195d
chore(docs): deprecated nexus/testing module #833 (#840) 2021-03-01 06:59:25 -05:00
Tim Griesser 27e35e51ee
fix: reduce iteration in connections (#843) 2021-02-24 13:25:56 -05:00
Alex Ruheni 3f78131abb
docs: vanilla nexus migration from nexus-plugin-prisma (#838)
* vanilla nexus migration

* added SDL converter section

* resolver object

* added blockquote referring to resolve section
2021-02-22 12:11:42 +01:00
Jason Kuhrt 06cd0a02e6 chore: yaml style 2021-02-19 23:51:28 -05:00
Jason Kuhrt 5d23123cc0 chore: tsconfig should exclude docs 2021-02-19 23:51:18 -05:00
hanbin8269 e3b414ddde
chore(docs): Fix Docs 061-list-nonNull.mdx (#837) 2021-02-18 15:04:08 -05:00
Rich Haines 43f1da24e7
chore(docs): Updated tutorial docs (chapter 5 persisting-data-via-prisma) to fix cli install instructions (#834) 2021-02-18 10:05:21 -05:00
Francis Gulotta 46ad529954
docs: add more information to field authorize plugin (#821)
Added some getting started information and a description of the field this plugin adds.
2021-02-08 14:52:04 -05:00
Gal Cegla 0f5f14526c
Update 05-chapter-4-testing-your-api.mdx (#803) 2021-02-03 19:05:16 -05:00
David c79d3cb9a9
asNexusMethod import is missing (#805) 2021-02-03 19:03:03 -05:00
Johnny Oshika d1634b2a58
Fix 'posts' field typo. Should be 'drafts'. (#806) 2021-02-03 19:01:54 -05:00
Johnny Oshika 29f03a03e7
Fix contextType export typo (from ContextModule to Context) (#807) 2021-02-03 19:00:46 -05:00
188 changed files with 11775 additions and 5163 deletions

View File

@ -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

View File

@ -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}}

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ examples/*/dist
website/static/playground-dist
yarn-error.log
coverage/*
tests/esm/out/

View File

@ -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

View File

@ -1,7 +1,7 @@
# Nexus
![trunk](https://github.com/graphql-nexus/nexus/workflows/trunk/badge.svg)
[![npm version](https://badge.fury.io/js/%40nexus%2Fschema.svg)](https://badge.fury.io/js/%40nexus%2Fschema)
[![trunk](https://github.com/graphql-nexus/nexus/workflows/trunk/badge.svg)](https://github.com/graphql-nexus/nexus/actions/workflows/trunk.yml)
[![npm version](https://badge.fury.io/js/nexus.svg)](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).

View File

@ -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.

View File

@ -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
```

View File

@ -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",

View File

@ -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

View File

@ -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 {

View File

@ -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
// ...

View File

@ -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!

View File

@ -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 {

View File

@ -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).

View File

@ -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 {

View File

@ -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).

View File

@ -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)
},
})
```

View File

@ -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:

View File

@ -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
})
```

View File

@ -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)`

View File

@ -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,
}
})
}
})
},
})
```

View File

@ -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

View File

@ -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

View File

@ -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', {

View File

@ -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:

View File

@ -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.

View File

@ -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:

View 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
}

View File

@ -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.
![](https://imgur.com/MK4FKyd.png)
## 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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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>

View File

@ -249,7 +249,6 @@ hr {
.list li {
padding-left: 20px;
text-indent: -1em;
margin-top: 10px;
}

View File

@ -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.

View File

@ -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"
}
}

View File

@ -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',

View File

@ -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"

View File

@ -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

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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
}

View File

@ -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",

View File

@ -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,

View File

@ -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',
})

View File

@ -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 {}
}

View File

@ -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
}
}

View File

@ -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"

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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"

View File

@ -1,4 +1,4 @@
import * as ts from 'typescript'
import type * as ts from 'typescript'
export interface ContextType {
source: ts.SourceFile

View File

@ -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)),
})
},
})

View File

@ -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
},

View File

@ -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' })
},
})

View File

@ -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
}

View File

@ -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"

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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"
}
}

View File

@ -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;
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -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'

View File

@ -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).
*/

View File

@ -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 })

View File

@ -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
}

View File

@ -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',

View File

@ -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,
}
})
}

View File

@ -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> {

View File

@ -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> {

View File

@ -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>

View File

@ -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> {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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 casesensitive 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 casesensitive 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>(

View File

@ -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' })

View File

@ -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
}

View File

@ -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)

View File

@ -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)) {

View File

@ -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_ timeas 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* timeas 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>

View File

@ -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 casesensitive 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>(

View File

@ -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' })

View File

@ -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
}

View File

@ -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 casesensitive 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>(

View File

@ -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)

View File

@ -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> {

View File

@ -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)

View File

@ -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)

View File

@ -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'

View File

@ -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
}
}

219
src/graphqlInternal.ts Normal file
View File

@ -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, '\\"""') + '"""'
}

View File

@ -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'

171
src/makeSchema.ts Normal file
View File

@ -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
}

8
src/node.ts Normal file
View File

@ -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,
}
}

View File

@ -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 */

View File

@ -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}`
}

View File

@ -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