@nexus/schema

$npm add @nexus/schema

Why Nexus?

Words from the original author, Tim Griesser;

Nexus Schema was born out of my experience building several production GraphQL APIs, in different languages and frameworks. The first with vanilla graphql-js, another schema-first with graph.ql and later graphql-tools. Following that with graphene-python and most recently with a bit of graphql-ruby.

After working with the toolkits in other scripting languages, it felt like there was a gap in the JavaScript approaches. Schema-first development starts out great, by simply expressing your schema in the GraphQL Schema Definition Language (SDL) and providing resolvers matching to the types as needed you are up and running fast! No need for tons of requires or "overhead" to get a GraphQL server running.

As your schema then grows to hundreds or thousands of types, manually curating these SDL fragments becomes tedious. Documentation changes can be tough. Modifying fields on interfaces can require manual changes to many implementing types, a process that can be quite error prone.

If only there were a way to combine the simplicity of schema-first development, with the long-term maintainability of a definition-first approach.

GraphQL Nexus aims to fill that void, making the process as simple as possible while also making good use of the runtime to introduce powerful ways of composing types, introducing type or schema wide changes, and much more.

The core idea of GraphQL Nexus draws from basing the schema off the SDL - keeping things declarative and simple to understand. It allows you to reference the type names as string literals rather than always need to import to reference types (you can do that too if you prefer).

By combining automatic type generation with some of the more powerful features of TypeScript - type merging, conditional types, and type inference, we can know exactly which type names we are referring to and able to use throughout our code. We can know both the parameters and the return type of resolvers without providing any type annotation. It takes a little getting used to, but it ends up leading to a great feedback loop of the types annotating themselves.

Type Generation Details

This is the most important piece to understand to get the most out of Nexus. It is relevant to JavaScript as well as TypeScript users, as tools like VSCode and // @ts-check can utilize these types to aid in autocomplete or type-checking. A core goal of Nexus is to have the best possible type coverage with the least possible manual type annotation.

Overview

Nexus was designed with TypeScript in mind. In order to fully typecheck our GraphQL objects, we need to generate a number of types that combine the schema, any type or field configuration provided, and the GraphQL resolution algorithm to create as much type-safety as possible without any additional work importing and assigning types throughout the codebase.

Root Types

A root type is a type representation of the value used to resolve the fields of an object type. It is the object that will be passed as the first argument of resolve. It can be a plain JS object, a database model, a mongoose document, a JS class, anything that fulfills the contract defined by the GraphQL object type, based on the field definitions.

Scalars can also have backing types, representing the value they are parsed into.

Sometimes GraphQL types are passthrough, and don't have a dedicated type backing them. One such case would be in the Edge of a Relay style pagination. In this case, Nexus will generate a type-definition which makes assumptions of the necessary value to fulfill the contract. If this is incorrect, you can always provide a concrete type for the object.

Field Type

A field type is the valid return value used to a field on an object type. In GraphQL, promises can be returned at every level of the type resolution, so we wrap the types in a MaybePromiseDeep<T> type to express this.

Configuring our types

The Ghost Example 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 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.

Edit this page on Github