Leonardo Maldonado Full Stack Developer. JavaScript, React, TypeScript, GraphQL.

How GraphQL Nexus can help you create better APIs

5 min read 1665

GraphQL is really becoming a standard when dealing with APIs. It’s helping a lot of companies and developers to thrive and create better APIs, consequently creating better applications for their clients. A lot of companies, including GitHub, Shopify, Airbnb, PayPal, have adopted GraphQL.

There are a lot of points and concepts that make GraphQL an easy choice when starting to create a new API. The idea is to ask only for what you want and get exactly that. This doesn’t always happen with a REST API. Basically, in REST APIs, when we want to get data, we receive a whole object instead of a specific part of the data that we want. Imagine that we want to get only the name of a specific user, to do that we would normally hit an endpoint to get the user’s data. The problem is that, if we don’t create a specific endpoint just to retrieve the user’s name, we will receive the whole user object, containing more data than we want.

Building a scalable GraphQL API is not easy, and it takes a lot of time, effort, and study. We need to know exactly which approach is going to be used, which technology, and how to design the schema correctly.

Essentially, GraphQL has two main approaches that we can use to create our APIs, the code-first approach, and the SDL-first approach (also known as schema-first approach).

In this article, we are going to learn about how we can use GraphQL Nexus a code-first and strongly-typed GraphQL schema construction to create better, type-safe, and declarative GraphQL APIs.

To have a better understanding of GraphQL Nexus, we need to understand a little bit about how these two approaches work.

Code-first vs. SDL-first

When GraphQL was released a few years ago, Facebook released a reference implementation for GraphQL APIs called graphql-js.

The graphql-js reference implementation uses the code-first approach, the GraphQL schema is created by using a plain JavaScript object:

import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql';

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'QueryType',
    fields: {
      helloWorld: {
        type: GraphQLString,
        resolve: () => 'Hello world!',
      },
    },
  }),
})

With the code-first approach, we define our schema in the code. The code-first approach, in a large codebase, can be really useful and helpful to organize and move fast with your GraphQL schema.

Although the code-first approach is a really good way to build your GraphQL API, many developers think that the code-first approach can be verbose to write. For a few developers, the idea to create your GraphQL schema using code is not visually easy to understand while implementing the schema.

We made a custom demo for .
No really. Click here to check it out.

The SDL-first (also known as schema-first) approach is perfect for that. In the SDL-first approach, we define the GraphQL schema using SDL (Schema Definition Language):

type Query {
  helloWorld: String!
}

The SDL is a well-defined way to build your schema, and it’s part of the official GraphQL specification. The schema-first approach makes it easier to understand and visualize in a clear way our GraphQL schema, making the process of creating the schema more intuitive.

One of the downsides is that we need to implement our resolvers separately from our GraphQL schema. Another downside that we can have with the SDL-first approach is that sometimes the usage of directives in the schema makes no sense.

Now that we know the differences between the code-first vs. SDL-first, we can learn more about GraphQL Nexus.

GraphQL Nexus

Created by Prisma, GraphQL Nexus is a declarative, code-first, and strongly typed GraphQL schema construction.

GraphQL Nexus is part of the Nexus framework, but it can be used on its own. To get started with it, all we have to do is install two dependencies:

yarn add @nexus/schema graphql

We need the graphql dependency here because GraphQL Nexus requires it as a peer dependency.

GraphQL Nexus was designed to combine both the simplicity of the SDL-first approach and the long-term and organization of the code-first approach. It was built upon graphql-js, so if you have used it before, you might notice some familiarity.

As the documentation says:

GraphQL Nexus was designed with TypeScript/JavaScript IntelliSense in mind, and combines TypeScript generics, conditional types, and type merging to provide full auto-generated type coverage out of the box.

Schema

After having installed GraphQL Nexus, to create your first schema all you have to do is import the makeSchema from GraphQL Nexus:

import { makeSchema } from '@nexus/schema';

The makeSchema function is used to define the GraphQL schema. You can combine GraphQL types defined with GraphQL Nexus or even pass types that you manually defined as types such as Scalar, ObjectType, Enum, etc.

Using the makeSchema function, is very similar to the graphql-js implementation. Create an object for your schema, and inside that object, you can pass two properties, types and outputs:

const schema = makeSchema({
  types: [Query],
  outputs: {
    schema: __dirname + '/generated/schema.graphql',
    typegen: __dirname + '/generated/typings.ts',
  },
});

The types property is where you can pass your GraphQL types. In the outputs property, you can pass two properties called schema and typegen. GraphQL Nexus is going to generate a file called schema.graphql for you with your entire GraphQL schema and a file called typing.ts with all of our types.

Of course, you can pass more than two properties inside the makeSchema function. GraphQL Nexus has a property called plugins, where you can have an array of plugins to extend the functionality of your GraphQL:

const schema = makeSchema({
  types: [Query, Mutation],
  outputs: {
    schema: __dirname + '/generated/schema.graphql',
    typegen: __dirname + '/generated/typings.ts',
  },
  plugins: {
    fieldAuthorizePlugin({
      ...
    }),
    nullabilityGuard({
      ...
    })
  }
});

Queries

To create a query using GraphQL Nexus, first you need to import the QueryType:

import { queryType, makeSchema } from '@nexus/schema';

After importing it you can create an object called Query where all your queries will be. You need to use a function called definition, and inside this function, you can use the t argument to define the type of response of your queries:

const Query = queryType({
  definition(t) {
    ...
  },
});

For example, let’s imagine that we wanted to create a query called user. If we wanted to return a string as an expected type of response of this query, we could do it like this:

const Query = queryType({
  definition(t) {
    t.string('user', {
      resolve: (root, args, context) => 'Hello!',
    });
  },
});

Notice that we used t.string to specify the type of response of the query. There are a variety of types that you can use to specify the type of response such as boolean, field, float, id, int, etc.

Mutations

Creating a mutation using GraphQL Nexus is really similar to queries, first, you need to import the MutationType:

const Mutation = mutationType({
  definition(t) {
    t.string('createUser', {
      resolve: (root, args, context) => 'Hello!',
    });
  },
});

One nice thing about GraphQL Nexus is that you can define arguments that can be used in any object or interface type:

import { stringArg } from '@nexus/schema';
const userArgs = {
  name: stringArg({
    required: true,
    description: 'The name of the user to be created'
  })
};

There are a lot of different argument functions that you can use such as arg, intArg, stringArg, floatArg, idArg, and booleanArg.

Types

The most important part of a GraphQL API is the object types. To create one using GraphQL Nexus, all you need to do is import the objectType.

import { objectType } from '@nexus/schema';

Creating an object type is very similar to creating a query or mutation. You can pass a property called name, to define the name of the type, and inside the definition function you define the properties of the type:

import { objectType } from '@nexus/schema';
const User = objectType({
  name: 'User',
  definition(t) {
    t.int('id');
    t.string('title');
    t.int('age');
  }
});

You can create other types such as Scalar, ObjectType, Enum very easily, GraphQL Nexus has nice support to work with those types:

import { scalarType } from '@nexus/schema';
const DateScalar = scalarType({
  name: 'DateScalar',
  description: 'Date custom scalar',
  ...
});

Another useful type is the inputObjectType. You can create an input object type that can be defined and passed as an input value, this is really helpful for mutations and queries when you have a complex input value:

import { inputObjectType } from '@nexus/schema';
const UserInputType = inputObjectType({
  name: 'UserInputType',
  description: 'User input type',
  definition(t) {
    t.string('id', { required: true });     
    t.string('name');
    t.int('age');
  }
});

Plugins

Another really nice feature that GraphQL Nexus has is plugins. GraphQL Nexus has a plugin API that allows you to define your abstractions when building your GraphQL Schema.

GraphQL Nexus also has a lot of plugins out of the box, such as:

  • Query complexity plugin —  measuring the query complexity inside your GraphQL schema is not a thing that a lot of developers do, but sometimes it’s necessary. A single query can generate a potential huge workload for your server. You can use this plugin to limit and keep track of your GraphQL operations, and define the field-level complexity values

GraphQL Nexus has a few more plugins that can be very useful to work with, such as:

  • Connection plugin – A plugin for a new object definition builder, it enables paginated association between types
  • Field authorize plugin – A plugin to define field-level authorization to a query
  • Nullability guard plugin – A plugin for guarding so that non-null values will not crash your queries in production

The code-first approach is, on some occasions, better than SDL-first. With GraphQL Nexus, we can benefit from this approach and create a more robust and well-defined GraphQL schema. Created upon the graphql-js implementation, GraphQL Nexus is one of the best options available to create a really good, robust, maintainable, and concise GraphQL API.

Conclusion

In this article, we first learned about the two main approaches that we have in GraphQL, code-first, and SDL-first. We also took a look at GraphQL Nexus, how to create your first schema, and define your queries and mutations. GraphQL ships with a lot of features such as plugins, which we can use to define our own abstractions and helps us to improve our GraphQL API.

Monitor failed and slow GraphQL requests in production

While GraphQL has some features for debugging requests and responses, making sure GraphQL reliably serves resources to your production app is where things get tougher. If you’re interested in ensuring network requests to the backend or third party services are successful, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic GraphQL requests to quickly understand the root cause. In addition, you can track Apollo client state and inspect GraphQL queries' key-value pairs.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Leonardo Maldonado Full Stack Developer. JavaScript, React, TypeScript, GraphQL.

2 Replies to “How GraphQL Nexus can help you create better APIs”

Leave a Reply