DEV Community

Cover image for Complete Introduction to Fullstack, Type-Safe GraphQL (feat. Next.js, Nexus, Prisma)
hexrcs for Prisma

Posted on • Edited on

Complete Introduction to Fullstack, Type-Safe GraphQL (feat. Next.js, Nexus, Prisma)

Complete Introduction to Fullstack, Type-Safe GraphQL (feat. Next.js, Nexus, Prisma)

In this post, you'll learn how to build––from scratch––an entirely type-safe, fullstack web app, using GraphQL with a database attached!

To follow along with the source code, clone this repo.

πŸ—“ Update 19/11/2020: The tutorial has been updated to use Nexus Schema as the Nexus Framework has been discontinued

Our tech stack

First, let's have a look at our tools of choice:

Let's get started! πŸš€

Step 0: Install VS Code extensions

Before we start, make sure that you have installed these VS Code extensions for syntax highlighting and auto-formatting - Prisma and GraphQL.

Prisma Extension for VS Code
Prisma Extension for VS Code

GraphQL Extension for VS Code
GraphQL Extension for VS Code

Step 1: Spin up a PostgreSQL database

The first thing you'll need is a PostgreSQL database instance to interact with during development.

There are many options for this, but Heroku allows us to host PostgreSQL databases for free with minimal setup required. Check out this post by Nikolas Burk guiding you through the process!

If you have Docker installed and would rather keep your development database local, you can also check out this video I did on how to do this with Docker Compose.

You will be able to get a PostgreSQL URI in this format:

postgresql://<USER>:<PASSWORD>@<HOST_NAME>:<PORT>/<DB_NAME>
Enter fullscreen mode Exit fullscreen mode

Note: if you are using Heroku, you'll get a URI with postgres instead of postgresql as the protocol. Both formats should work but we'd prefer to use postgresql.

When everything is set up properly, you're good to move on to the next step! πŸ˜ƒ

Step 2: Create a Next.js project

Now, create a Next.js project with create-next-app and enter the directory:

npx create-next-app my-awesome-app --use-npm -e with-typescript
cd my-awesome-app
Enter fullscreen mode Exit fullscreen mode

Git should be automatically initialized by create-next-app, and your project structure should look like this:

Project structure bootstrapped by  raw `create-next-app` endraw
Project structure bootstrapped by create-next-app

The create-next-app might not have the latest TypeScript package pre-installed. If you want to use the latest TypeScript features, run npm install -D typescript@latest after the project is bootstrapped.

Step 3: Install Nexus with Prisma

With the Next.js project ready, open a terminal window at the root of the application and install both Nexus Schema and Prisma.

For Prisma, we need @prisma/client, @nexus/schema and nexus-plugin-prisma as a regular dependencies and @prisma/cli as a dev dependency.

Regular dependencies:

npm i @prisma/client @nexus/schema nexus-plugin-prisma
Enter fullscreen mode Exit fullscreen mode

Dev dependencies:

npm i @prisma/cli
Enter fullscreen mode Exit fullscreen mode

Once the dependencies are installed, initialize Prisma in the project.

npx prisma init
Enter fullscreen mode Exit fullscreen mode

This command will create a prisma directory. If you have a look inside, you'll see a .env file and a schema.prisma file. The schema.prisma file will hold the database model and the .env file will hold the database connection string.

Because the database connection string contains sensitive information, it's a good practice to never commit this .env file with Git, so make sure it's also added to the .gitignore file.

Adjust the schema.prisma file to include a User model:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id   String @default(cuid()) @id
  name String
}
Enter fullscreen mode Exit fullscreen mode

The schema file tells Prisma to use PostgreSQL as the database type, and the database connection URL is defined as an environment variable. It also defines a simple User data model with an id and a name field.

Your project should currently look like this:

Project structure after creating the  raw `prisma` endraw  directory
Project structure after creating the prisma directory

Step 4: Wire up Nexus Schema with Next.js

Nexus Schema is a library that allows us to build code-first GraphQL APIs. It's our responsibility to bring along a server to serve that API. For our purposes, we'll use Apollo Server.

There are several varieties of Apollo Server that are used for various purposes. For our project, we'll want apollo-server-mirco as it's well-suited to serverless deployments.

npm install apollo-server-micro
Enter fullscreen mode Exit fullscreen mode

To create a GraphQL endpoint, create a new file in your project at /pages/api/graphql.ts. Thanks to the powerful API routes in Next.js, the GraphQL server will be accessible at http://our-app-domain/api/graphql when the Next.js server is started.

In the /pages/api/graphql.ts file, write the following boilerplate code:

import { ApolloServer } from 'apollo-server-micro';

// we'll create these in a second!
import { schema } from '../../graphql/schema';
import { createContext } from './../../graphql/context';

const apolloServer = new ApolloServer({
  context: createContext,
  schema,
  tracing: process.env.NODE_ENV === 'development'
});

export const config = {
  api: {
    bodyParser: false
  }
};

export default apolloServer.createHandler({
  path: '/api/graphql'
});
Enter fullscreen mode Exit fullscreen mode

Since everything inside the /pages/api/ directory is considered as an API route, it's a good idea to implement the actual schema and resolvers outside this directory.

Now, create a new directory in the project root called /graphql/ and two files within: /graphql/schema.ts and /graphql/context.ts.

Inside /graphql/schema.ts, start by using the makeSchema function to construct a GraphQL schema with Nexus. We'll also want to use nexus-plugin-prisma with the CRUD feature enabled:

// graphql/schema.ts

import { objectType, queryType, mutationType, makeSchema } from '@nexus/schema';
import { nexusPrisma } from 'nexus-plugin-prisma';
import path from 'path';

const Query = queryType({
  definition(t) {
    t.string('hello', { resolve: () => 'hello world' });
  }
});

export const schema = makeSchema({
  types: [Query],
  plugins: [nexusPrisma({ experimentalCRUD: true })],
  outputs: {
    typegen: path.join(process.cwd(), 'generated', 'nexus-typegen.ts'),
    schema: path.join(process.cwd(), 'generated', 'schema.graphql')
  },
  typegenAutoConfig: {
    contextType: 'Context.Context',
    sources: [
      {
        source: '@prisma/client',
        alias: 'prisma'
      },
      {
        source: path.join(process.cwd(), 'graphql', 'context.ts'),
        alias: 'Context'
      }
    ]
  }
});
Enter fullscreen mode Exit fullscreen mode

The call to makeSchema includes a property called plugins. This is an array of any plugins we want to use with Nexus Schema and, in our case, we want to use nexus-plugin-prisma. The configuration we have here tells the plugin to use the CRUD feature which is what allows us to have automatically-generated CRUD resolvers for our API. You can read more on the CRUD feature provided by Nexus Schema.

Next, initialize the PrismaClient within /graphql/context.ts and export a function to create the context in Apollo Server.

// graphql/context.ts

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export interface Context {
  prisma: PrismaClient;
}

export function createContext(): Context {
  return { prisma };
}
Enter fullscreen mode Exit fullscreen mode

The file structure should now look something like this:

my-awesome-app/
β”œβ”€ components/
β”œβ”€ graphql/
β”‚  β”œβ”€ context.ts
β”‚  β”œβ”€ schema.ts
β”œβ”€ interfaces/
β”œβ”€ pages/
β”œβ”€ prisma/
β”‚  β”œβ”€ .env
β”‚  β”œβ”€ schema.prisma
β”œβ”€ utils/
β”œβ”€ next-env.d.ts
β”œβ”€ package-lock.json
β”œβ”€ package.json
β”œβ”€ tsconfig.json
Enter fullscreen mode Exit fullscreen mode

With these files in place, run the application:

npx next dev
Enter fullscreen mode Exit fullscreen mode

If you go to http://localhost:3000/api/graphql, you'll see the GraphQL Playground up and running (with our "hello world" schema)! πŸ˜ƒ

GraphQL Playground with a hello world schema
GraphQL Playground with a hello world schema

Step 5: Implement your first GraphQL API

With the GraphQL server running in the background and the GraphQL Playground ready at http://localhost:3000/api/graphql, it's time to start implementing the API!

Step 5.1: Define an Object type

Start by defining a User object type to reflect the database schema. Once defined, add it to the types array in makeSchema.

// graphql/schema.ts

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

const User = objectType({
  name: 'User',
  definition(t) {
    t.model.id();
    t.model.name();
  }
});

// ...

export const schema = makeSchema({
  types: [User, Query]
  // ...
});
Enter fullscreen mode Exit fullscreen mode

If you are typing the code above instead of copy n' pasting, you will notice that VS Code will autocomplete the fields (id, name) that are available on the User data model defined earlier in /prisma/schema.prisma.

Tip: You can also always invoke intellisense with CTRL + SPACE, in case it doesn't automatically show up sometimes, super helpful!

Now, go back to the GraphQL Playground and toggle the Schema side panel - you will see a GraphQL object type User is generated from the code you just wrote in the /graphql/schema.ts file.

type User {
  id: String!
  name: String!
}
Enter fullscreen mode Exit fullscreen mode

Step 5.2: Define the Query type

For the root Query type, Nexus provides a queryType function.

To query a list of existing users in the database, you can write a resolver for allUsers field as follows:

const Query = queryType({
  definition(t) {
    t.list.field('allUsers', {
      type: 'User',
      resolve(_parent, _args, ctx) {
        return ctx.prisma.user.findMany({});
      }
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

You can do whatever you want in the resolve function. The Prisma client for your database can be directly accessed as the db property on the ctx object. You can read more about the API of Prisma Client in its official documentation.

Tip: You can always use the intellisense feature in VS Code to explore the APIs of Nexus and Prisma Client!

In addition to manually writing resolvers, the Nexus-Prisma plugin conveniently exposes basic "read" operations on the database on t.crud. The following code will let you find a User (or a list of Users) from the database directly.

const Query = queryType({
  definition(t) {
    t.list.field('allUsers', {
      type: 'User',
      resolve(_parent, _args, ctx) {
        return ctx.prisma.user.findMany({});
      }
    });
    t.crud.user();
    t.crud.users();
  }
});
Enter fullscreen mode Exit fullscreen mode

The code above will generate a GraphQL root Query type:

type Query {
  allUsers: [User!]
  user(where: UserWhereUniqueInput!): User
  users(
    skip: Int
    after: UserWhereUniqueInput
    before: UserWhereUniqueInput
    first: Int
    last: Int
  ): [User!]!
}

input UserWhereUniqueInput {
  id: String
}
Enter fullscreen mode Exit fullscreen mode

Notice that all the related Input types are also generated for us for free! πŸ’―

 raw `Query` endraw  type is generated by Nexus
Query type is generated by Nexus

Step 5.3: Define the Mutation type

Similar to the Query type, a Mutation type can be defined with the mutationType function.

😈 Let's have some fun and create a bigRedButton mutation to destroy all user data in the database.

We also have access to the t.crud helper here, which exposes the basic "create", "update" and "delete" operations on the database. We then must add Mutation to the types array in makeSchema.

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

// ...

const Mutation = mutationType({
  definition(t) {
    t.field('bigRedButton', {
      type: 'String',
      async resolve(_parent, _args, ctx) {
        const { count } = await ctx.prisma.user.deleteMany({});
        return `${count} user(s) destroyed. Thanos will be proud.`;
      }
    });

    t.crud.createOneUser();
    t.crud.deleteOneUser();
    t.crud.deleteManyUser();
    t.crud.updateOneUser();
    t.crud.updateManyUser();
  }
});

// ...

export const schema = makeSchema({
  types: [User, Query, Mutation]
  // ...
});
Enter fullscreen mode Exit fullscreen mode

This will generate a GraphQL schema like below:

type Mutation {
  bigRedButton: String
  createOneUser(data: UserCreateInput!): User!
  deleteOneUser(where: UserWhereUniqueInput!): User
  deleteManyUser(where: UserWhereInput): BatchPayload!
  updateOneUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User
  updateManyUser(
    data: UserUpdateManyMutationInput!
    where: UserWhereInput
  ): BatchPayload!
}
Enter fullscreen mode Exit fullscreen mode

 raw `Mutation` endraw  type is generated by Nexus
Mutation type is generated by Nexus

Now, our simple but fully-featured GraphQL API is ready! πŸ₯³

Step 6: Initialize the database

Before you can do anything with your GraphQL API, you'll need to create tables in the database corresponding to the Prisma schema file.

This can be done by manually connecting to the database and running SQL commands, but I'll show you how to do it with the prisma db push commnand - the database tool that's part of Prisma 2.

First, save the initial changes of our schema.prisma file with the command below. At the moment, the prisma db push command is still in a preview state, so the extra flag --preview-feature flag is needed.

npx prisma db push --preview-feature
Enter fullscreen mode Exit fullscreen mode

Awesome! With the database prepared, it's time to go back to http://localhost:3000/api/graphql, and have some fun with the your first GraphQL API with Nexus. Let me give you an example to play with!

mutation {
  createOneUser(data: { name: "Alice" }) {
    id
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing out user creation 😎
Testing out user creation 😎

Step 7: Set up Urql GraphQL client with Next.js

We'll use Urql as the GraphQL client on the frontend, but you can use any library you like.

First, install the dependencies:

npm install graphql-tag next-urql react-is urql isomorphic-unfetch
Enter fullscreen mode Exit fullscreen mode

Then, create a new file at /pages/_app.tsx. This is a special Next.js component that will be used to initialize all pages.

import React from 'react';
import { withUrqlClient, NextUrqlAppContext } from 'next-urql';
import NextApp, { AppProps } from 'next/app';
import fetch from 'isomorphic-unfetch';

// the URL to /api/graphql
const GRAPHQL_ENDPOINT = `http://localhost:3000/api/graphql`;

const App = ({ Component, pageProps }: AppProps) => {
  return <Component {...pageProps} />;
};

App.getInitialProps = async (ctx: NextUrqlAppContext) => {
  const appProps = await NextApp.getInitialProps(ctx);
  return { ...appProps };
};

export default withUrqlClient((_ssrExchange, _ctx) => ({
  url: GRAPHQL_ENDPOINT,
  fetch
}))(
  // @ts-ignore
  App
);
Enter fullscreen mode Exit fullscreen mode

And that's it! Now you can use the GraphQL client in any page in your Next.js app.

Step 8: Use the GraphQL client

First, create a TSX file at /components/AllUsers.tsx. This file will have a component that performs an allUsers GraphQL query and renders the result as a list. This way, we can use the component to fetch all the user info from our PostgreSQL database.

You can create the query first, for example, with the following code. By using gql, the GraphQL VS Code extension will be able to identify the template string as a GraphQL query and apply nice syntax highlighting to it.

import React from 'react';
import gql from 'graphql-tag';
import { useQuery } from 'urql';

const AllUsersQuery = gql`
  query {
    allUsers {
      id
      name
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

Since it's known that the data you are going to get is an array of User objects (thank you, GraphQL schema!), you can also define a new type:

type AllUsersData = {
  allUsers: {
    id: string;
    name: string;
  }[];
};
Enter fullscreen mode Exit fullscreen mode

Next, create the React component that will be using the query.

The component encapsulates the following logic:

  • If the query is still in a fetching state, the text "Loading..." will be returned
  • If an error is occurred during the process, we will display the error
  • If the query is no longer fetching and there's no error, the data will be used to render a list of users
const AllUsers: React.FC = () => {
  const [result] = useQuery<AllUsersData>({
    query: AllUsersQuery
  });
  const { data, fetching, error } = result;

  if (fetching) return <p>Loading...</p>;
  if (error) return <p>Oh no... {error.message}</p>;

  return (
    <div>
      <p>There are {data?.allUsers.length} user(s) in the database:</p>
      <ul>
        {data?.allUsers.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default AllUsers;
Enter fullscreen mode Exit fullscreen mode

Now, save the TSX file, and mount it onto the home page /pages/index.tsx:

import Link from 'next/link';
import Layout from '../components/Layout';
import AllUsers from '../components/AllUsers';

const IndexPage = () => (
  <Layout title="Home | Next.js + TypeScript Example">
    <h1>Hello Next.js πŸ‘‹</h1>
    <p>
      <Link href="/about">
        <a>About</a>
      </Link>
    </p>
    {/* === Tada! === */}
    <AllUsers />
  </Layout>
);

export default IndexPage;
Enter fullscreen mode Exit fullscreen mode

Time to spin up the Next.js dev server!

npm run dev
Enter fullscreen mode Exit fullscreen mode

VoilΓ ! The user list is rendered! πŸ₯³

User list is rendered on our home page
A user list is rendered on our home page

Step 9: Auto-generate useQuery hooks and types

Instead of manually defining all the types we expect to receive via GraphQL, we can also use a very cool package GraphQL Code Generator to generate types directly from the Nexus GraphQL endpoint. This way, you essentially only have to define the types once in the schema.prisma file as the single-source-of-truth, then all types you'll use in the application can be derived from that schema with little manual effort! πŸŽ‰

First, copy and refactor the GraphQL queries from the TSX files into the graphql directory. With the example from Step 8, create a new file at /graphql/queries.graphql.ts and copy the query from /components/AllUsers.tsx:

import gql from 'graphql-tag';

export const AllUsersQuery = gql`
  query AllUsers {
    allUsers {
      id
      name
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

Separating GraphQL operations from components makes it easier to navigate the codebase.

Next, install the packages needed by graphql-code-generator as dev dependencies:

npm install -D \
    @graphql-codegen/cli \
    @graphql-codegen/typescript \
    @graphql-codegen/typescript-operations \
    @graphql-codegen/typescript-urql
Enter fullscreen mode Exit fullscreen mode

Then, create a codegen.yml file in the project root with the following content:

overwrite: true
schema: 'http://localhost:3000/api/graphql' # GraphQL endpoint via the nexus dev server
documents: 'graphql/**/*.graphql.ts' # parse graphql operations in matching files
generates:
  generated/graphql.tsx: # location for generated types, hooks and components
    plugins:
      - 'typescript'
      - 'typescript-operations'
      - 'typescript-urql'
    config:
      withComponent: false # we'll use Urql client with hooks instead
      withHooks: true
Enter fullscreen mode Exit fullscreen mode

The configs above will tell graphql-code-generator to pull the GraphQL schema from http://localhost:3000/api/graphql, then generate types, Urql useQuery hooks into a file located at /generated/graphql.tsx.

Cool, let the code generation begin (in watch mode)!

npx graphql-codegen --watch
Enter fullscreen mode Exit fullscreen mode

You will see some nice, written-by-a-robot code in /generated/graphql.tsx. How neat!

A  raw `useAllUsersQuery` endraw  hook and all the types are generated by GraphQL Code Generator
A useAllUsersQuery hook and all the types are generated by GraphQL Code Generator

Now, you can go back to components/AllUsers.tsx, and replace the manually written AllUsersData type, the GraphQL query, and the useQuery hook, with what's in the /generated/graphql.tsx file:

import React from 'react';
import { useAllUsersQuery } from '../generated/graphql';

const AllUsers: React.FC = () => {
  const [result] = useAllUsersQuery();
  const { data, fetching, error } = result;

  if (fetching) return <p>Loading...</p>;
  if (error) return <p>Oh no... {error.message}</p>;

  return (
    <div>
      <p>There are {data?.allUsers?.length} user(s) in the database:</p>
      <ul>
        {data?.allUsers?.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default AllUsers;
Enter fullscreen mode Exit fullscreen mode

Revisit the index page of the app at http://localhost:3000, everything works like a charm! πŸ™Œ

To make the development experience even better, let's optimize the NPM scripts for the project.

First, install the Concurrently NPM module, which is a great tool for running multiple CLI watchers at the same time:

npm install -D concurrently
Enter fullscreen mode Exit fullscreen mode

Then, replace dev script in the package.json file with the following:

{
  // ...
  "scripts": {
    // ...
    "dev": "concurrently -r \"npx nexus dev\" \"npx next\" \"npx graphql-codegen --watch\""
    // ...
  }
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Now, we can use a single npm run dev command to launch Nexus, Next.js, and GraphQL Code Generator, all at the same time!

Conclusion

I hope you have enjoyed this tutorial and have learned something useful! You can find the source code in this GitHub repo.

Also, check out the Awesome Prisma listΒ for more tutorials and starter projects in the Prisma ecosystem!

Top comments (30)

Collapse
 
sleventyeight profile image
J. C.

This is a great tutorial!

Any chance you could do another post using this same setup but also including RBAC authentication (maybe with next-auth.js.org/) or some other httpOnly cookie solution?

Collapse
 
hexrcs profile image
hexrcs

Sounds like a good idea for a followup post! I'll probably use the nextjs-auth0 package instead of next-auth, or a roll-your-own solution.

Collapse
 
daniz profile image
Daniel Zaremba

I'm definitely voting for a roll-your-own tutorial!
Thanks for the great post!

Collapse
 
dance2die profile image
Sung M. Kim

Awesome tutorial, Xiaoru. Thank you.

Collapse
 
hexrcs profile image
hexrcs

Glad you liked it! πŸ™Œ

Collapse
 
nathanking profile image
Nathan King • Edited

OMG, I'm sorry to bother again but I when I define the graphql schema, I have these extra scalars and t.crud.user()/users() is not generating the Query properly, is not generating the input at all and two scalars are showing up. I defined a dateTime scalar in a previous project. Is this a caching problem?

Collapse
 
hexrcs profile image
hexrcs

Could you provide more details of the error message? Also could you check the versions of your Nexus and Nexus Prisma plugin? There was an update for Nexus after the initial version of this post, and the CRUD helpers now are disabled by default.

I updated this tut and the example repo, please check out this PR and let me know if it solves the problems!

BTW there's no global caching as far as I know, so there shouldn't be a problem if you defined some types in another project.

Collapse
 
nathanking profile image
Nathan King

I tried to run "npx nexus dev" at the start of the tutorial, I have recreated the project twice but I keep on getting this error:


4596 βœ• nexus:glocal The global Nexus CLI you invoked could not hand off to your project-local one becuase it wasn't on disk.

This can happen for example after you have cloned a fresh copy of your project from a remote repository.

Please install your dependencies and try your command again.

Location of the Nexus CLI you invoked: C:\Users\natha\Documents\Web Development\NEXT\nexus-prisma\node_modules\nexus\dist\cli\main.js

Location of your project-local Nexus CLI: C:\Users\natha\Documents\Web Development\NEXT\nexus-prisma\node_modules.bin\main.js

Collapse
 
godie99 profile image
Juan Diego

Try installing the versiΓ³n 0.26.0-next.5 of nexus

Collapse
 
hexrcs profile image
hexrcs

Looks like this can happen on Windows. Are you using the native Windows cmd.exe or WSL?

Collapse
 
nathanking profile image
Nathan King • Edited

Windows cmd... I should probably migrate over to WSL

Thread Thread
 
hexrcs profile image
hexrcs • Edited

Windows support is coming soon to Nexus! However, I'd still recommend you migrate over to WSL or use a Linux dev container of some kind, because most web dev tools and frameworks are created Unix-first.

Thread Thread
 
nathanking profile image
Nathan King

I'm in the process of downloading WSL 2 now 😊 I was going to make the switch anyway but just put it off. I'll try your tute again once it's set up πŸ‘

Thread Thread
 
nathanking profile image
Nathan King • Edited

Just to follow up, I decided to move to Linux and everything worked well!

Thanks so much for the amazing tutorial, it's awesome now that I've made it :P

(Maybe add a disclaimer for Windows users that they might have trouble, if you want)

Thread Thread
 
hexrcs profile image
hexrcs

Nice, great to hear that! :D

Collapse
 
melanke profile image
Gil LB • Edited

Man this Tutorial is great, very well explained with advanced features that a newbie like me could understand, great Job!
I just want to let you know that I was very confused about the fact that I changed my schema.prisma and the Nexus didn't process the change, I had a big trouble until I understand I had to call npx prisma generate to reload the content, so you might want to add this little note on the tutorial, idk.

Also, I have a question: The useAllUsersQuery is being processed via SSR by Next.js right? That is awesome because I don't need to type getServerSideProps. But who does this magic? And can I change this behaviour to work like getStaticProps?

Thank you for this amazing work you did, this is going to be the basis for a lot of projects in my company <3

Collapse
 
jbuck94 profile image
Jamie Buck

Curious about this as well - this code reads like all the GQL queries are happening client-side. Is that correct?

Collapse
 
melanke profile image
Gil LB

Hello @hexrcs can you help us on this? Pleeease πŸ˜… Thanks

Collapse
 
jmales profile image
Jmales

Thanks for the tutorial, lot's of great info!

I'm confused about something though. You said that using graphql-code-generator our source of truth is schema.prisma.

Maybe I'm doing something wrong but I still need to define my objectTypes, queryTypes, mutationTypes and then have my basic queries somewhere right?

import { schema } from 'nexus';

schema.objectType({
  name: 'User',
  definition(t) {
    t.model.userId();
    t.model.name();
  },
});

schema.extendType({
  type: 'Query',
  definition(t) {
    t.crud.user();
    t.crud.users({ filtering: true, ordering: true });
  },
});

schema.extendType({
  type: 'Mutation',
  definition(t) {
    t.crud.createOneUser();
  },
});
Enter fullscreen mode Exit fullscreen mode

And the queries:

export const UsersQuery = /* GraphQL */ `
  query UsersQuery {
    users {
      userId
      name
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

I feel that I'm writing a lot of redundant code

Collapse
 
hexrcs profile image
hexrcs

Hi there! The source of truth of GraphQL is not schema.prisma, but the GraphQL endpoint powered by Nexus.

You still need to clearly define how the GraphQL schema looks like (eg. object types), because of security (you don't want to expose everything from your DB, think ID and password) and flexibility (you can write your own resolver instead of being restricted to basic CRUD operations on the database).

However, the Nexus-Prisma plugin provides some simple .crud helpers as you can see from the examples, if you just want to directly expose CRUD operations to your GraphQL API. πŸ˜„

Collapse
 
soosap profile image
@soosap

Hey mate, great tutorial. There is one shortcoming in this setup. You are no longer able to spread your nexus schema definition into multiple files. One of the big advantages of the nexus framework is that you no longer need to pull all the dependencies into the schema file manually. This continues to work on localhost:4000/graphql but it no longer works for localhost:3000/api/graphql. The latter endpoint only continues to work when all the objectType, queryType, mutationType, etc. are defined in graphql/schema.ts.

You can try by adapting your repo. Just try to put the objectType for user in a different file. It will break the localhost:3000/api/graphql while localhost:4000/graphql remains in tact. Do you know a workaround for this!

Collapse
 
hexrcs profile image
hexrcs • Edited

Hi there, thanks for the comment! I'm actually not able to reproduce this - perhaps you forgot to import the files where other parts of the schema are defined?

What I did:

  1. Extracted the code for objectType User to another file located at graphql/newFile.ts - next to the original schema.ts
  2. Wrote import "./newFile.ts" at the beginning of graphql/schema.ts

Note that schema from Nexus (is a singleton I believe) is also imported in newFile.ts, and nothing is exported in that file. Here's what I have:

import { schema } from "nexus";

schema.objectType({
  name: "User",
  definition(t) {
    t.model.id();
    t.model.name();
  },
});

In JS/TS, if you import a file, it is scanned through and run by the interpreter, so schema.objectType() is actually executed when the interpreter is done with the line import "./newFile.ts", so it's not static.

Collapse
 
soosap profile image
@soosap • Edited

Yes, that's exactly what I am talking about,

you now have to write import statements for all schema related files in graphql/schema.ts.

If you do that then both GraphQL endpoints (localhost:4000/graphql + localhost:3000/api/graphql) work. However, when you omit the manual import statements in graphql/schema.ts it still continues to work at localhost:4000/graphql, however it no longer works for localhost:3000/api/graphql. For me not dealing with imports in graphql/schema.ts seemed like a nice behaviour of the nexus framework. I was trying to understand if I could avoid the import statements somehow in nextjs localhost:3000/api/graphql?

Thread Thread
 
hexrcs profile image
hexrcs • Edited

I'm not sure why it would continue to work with Nexus dev server's /graphql route, but my guess is that it's due to some caching not being invalidated by Nexus (probably a bug, I'll try to dig into it). The correct behavior should be it fails as well. :)

Edit: Normally, Nexus actually scans the entire project for import {schema} from 'nexus', if an entry point is not specified.

I think the problem you are encountering is because somehow Nexus imported the external schema after the Nexus app has assembled. Actually, when working with Next.js, it's not recommended to have multiple files that import the same part (like use, schema) from Nexus.

Collapse
 
armaandh profile image
Armaan Dhanji

Awesome tutorial! Are you going to be finishing up your egghead video series on Prisma?

Collapse
 
hexrcs profile image
hexrcs

Working on it! :D

Collapse
 
armaandh profile image
Armaan Dhanji

Thanks so much, I absolutely love your videos on Prisma!

Collapse
 
kouatchres profile image
KOUATCHOUA MARK

Wonderful tutorial, Xiaoru, how you have opened my eyes to a great deal of things and forced me to transition from prisma 1 to prisma 2 and God alone knows how my work has been simplified. Esp with the graphql-codegen. My heartfelt congratulations for the work.

Collapse
 
mdirshaddev profile image
Md Irshad

This tutorial is awesome but I have a question how we can pass request, response within the context and do I have to pass in both Apollo server and nexus makeSchema. Please help me out. Recently I found this hard to understand the logic behind of context.