- To get started with
building our GraphQL API, we first need to install
a couple of dependencies. So first we'll need to
install Apollo Server Micro. So Apollo Server is the GraphQL server which will serve the GraphQL endpoint. And we also need to install
GraphQL as a peer dependency and Micro cores, and we will see why exactly we need it. Now, we're not just
installing Apollo Server, we're installing Apollo Server Micro, because this is the Micro
implementation of Apollo Server which is optimized for serverless
environments and Next.js. Let's just run it. After that's done installing, in the root folder, create a new folder and call it GraphQL. This folder will contain
everything related to our GraphQL API. So inside this folder, create a new file and call it schema.ts. And in this file, we will define the GraphQL
schema for the app. So the first thing we
will do is import GQL from Apollo Server Micro, and this GQL, this will, we'll use it to actually
add syntax highlighting for our scheme. So what you need to say is just say export const typeDefs, because we are doing type definitions, which is just our schema, equals gql, and then have two backticks, and here we will define our schema. So I just added the schema. This is what a GraphQL schema looks like. So let's break it down. The first thing that we
have is an object type, and this object type is like an object that the API can return. And for our use case, we want to have a link object type because we want to be
able to return a link. So what we're doing is we're
saying the link object type will have the following fields. It has an ID, a title, a description, a URL, a category, an image URL, and a users field, which
is an array of strings. This will be an array of type user, but we haven't defined
the user object type yet, but we'll do that later. So yeah, we have these different fields along with their types, and now we have this type query. Now, the query type is a special type. This is the entry point
for our GraphQL API. So to actually be able to send queries, we need this query type. Now, in GraphQL, for read operations where
you're just requesting data or reading data, you would define these using queries. And on the other hand, you would say type
Mutation for any mutations. So these mutations are rights. So if you want to create,
update, or delete something, you would create a mutation for it. So right now we just have a single query and the query object type, and it's called Links. And what we're saying is
that this query will return an array of type link. And this exclamation
point is just saying that this query is non-nullable, which means we will
always return something. We cannot return null. So yeah, this is an example
of a simple GraphQL schema for our app. But as you can see, we
just defined the schema. We actually haven't described kind of the implementation detail. We didn't say what happens
when this query actually runs, and that is done using resolvers. So in the GraphQL folder, create a new file and
call it resolvers.ts. And what we'll do is just
say export const resolvers. This will be an object. Inside it there will be
an object called query. And then we will mention our links query. So this, the resolvers object
must have the same shape as our schema. So this is, this will be a function. And for now let's just return an array. So here we have this links field, and in our resolver, we must have the same name
so that when the query runs, we'll be able to run the logic
that's inside this function or this resolver. So now let's just save. And in the resolver, what we want to do is instead of returning just an empty array, let's actually return links, but let's hardcode this data. So let me just copy just,
I don't know, three links, and in the resolver, just add them. And now whenever we run the query, and we'll see how to do
that using Apollo Studio, whenever we run the links query, we will always get these three objects. So we will have an array with
these three objects inside it. And yeah, now the next step is actually combining the schema with the resolvers, and that is where Apollo Server comes in. So right now we actually
haven't created it yet, and that's what we'll do now. So Next.js is a full stack framework, and it has a really cool feature, and it's API routes. So when you have inside the pages folder, when you create a folder called API, each TypeScript or JavaScript
file in this API folder is automatically a route. So it's automatically an endpoint. So for example, if you wanted to define
kind of like a Rest API, you would have an endpoint here and call just links and dot TS or JS. And then in this function, you will make, maybe run a Prisma query to return data from your database. But for our app, we're not using just different endpoints. We will have a single endpoint, and we'll call it GraphQL. And this endpoint will
serve our GraphQL API. So whenever we want to
send queries or mutations, we will be sending them to, well, for now it's localhost:3000/api/graphql or the URL of your
deployed app /api/graphql. Now, if you wanted to make this just /api, you would rename this file to index.ts. But personally, I prefer
just making it explicit that we're using GraphQL. Now, to create the GraphQL endpoint, what we first need to do is
actually import Apollo Server from Apollo Server Micro. And we also want to import the resolvers and type definitions that we created. So let's just say import typeDefs from, and then just say dot dot
slash and dot dot slash again the GraphQL folder slash schema. And then we want to import the resolvers, so I'll just say important resolvers from, and then again the same directory
as the type definitions, and then say resolvers. And now we want to
instantiate Apollo Server. So just say const apolloServer
equals new ApolloServer, and this will take the
typeDefs that we defined along with the resolvers. And we also want to define a
function to start the server. So this is a requirement
for Apollo Server 3. You need to have a function
that starts the server before actually kinda
creating the endpoint. So let's just call it start server equals apolloServer.start. And now what we need to do, so right now, Next.js is not
yet aware of this endpoint. We actually need to make a default export, and we will just say export
default async function. And let me just write correctly. And say export default async function, and this function, we'll
just call it handler. It will take a request and response. And here this function, what we'll do is just
say await startServer, and then say await
apolloServer.createHandler, and this takes an object, and we will specify a path. So for us, it's going to be /api/graphql. So we're specifying the
path of our GraphQL server. And then we just pass
the request and response. Now, if we actually save and run the app, we will run into an issue. So Apollo Server 3 comes with, actually, it doesn't come installed with GraphQL Playground anymore. It redirects you to Apollo Studio, and Apollo Studio is hosted
at studio.apollographql.com. So if you actually try
to make any requests, you run into CORS issues, and that's why we're
installing Micro CORS. So to actually set up
CORS for our endpoint, just say import Cors from micro-cors, which we installed, and then just say const cors equals Cors. And what we want to do now
is actually wrap our function with this CORS variable. So just say cors and then do that. And actually, no, it's right here. Now, the final thing that we want to do is actually this able body parsing. That's because it is handled
by default in GraphQL. So to do that, we can just say export const config. This is Next.js specific. Equals an object. Inside it we'll say api
and then say bodyParser, then say false. And yeah, now we will have a GraphQL API. So if I actually start
the development server by saying npm run dev and I go to localhost:3000/api/graphql, we'll see what we get. So now if we go to
localhost:3000/api/graphql, I will get this page, and it says, "Ready to
explore your graph?" And we can then query our server. And this will redirect us from
localhost:3000/api/graphql to studio.apollographql.com. Actually, there is one thing that we actually need to define, and it's to add this line where we say if request.method equals options, to just do res.end and return
false for this to work. And return false. So actually now, if we
click query your server, we will be redirected to
studio.apollographql.com. I already have some
queries that we do not need and a couple of variables. So this is the GraphQL playground. And since we're working inside, kinda like we're working with GraphQL, we kinda just have documentation. So we can see the different
queries that we have. So far we only have one query
and it's the links query, and we can see that this query
returns the link object type, it returns an array of type links, and it's non-nullable. And actually, if we click on it, we will see that it's
actually automatically added for us here. We can see query, example query, and then links. This is the name of our query. And then we're specifying the
fields that we actually want returned for us. So now we have ID. We can include title, description,
URL, category, image URL, and let's just say that's
it and run this query. We will get the following results. Now, you can actually display
results either in a table or just the JSON. I find the table view
is just more readable. But right now these are the three links. So our GraphQL API works
because in our resolver, this is the data that we defined. So now we can actually also see a couple of useful information. So we can see the status of our query, we can see how long it took, and we can also download
the content as CSV or as JSON or copy it. So this is Apollo Studio
and it's really cool. Now, for GraphQL, what is super nice about it as, so I mentioned that it solves the overfetching or underfetching problem. So we requested all the
fields that we have for link, but say, for example, we actually remove the
description and the URL and run it again. So right now you can see we have the ID, the title, description,
URL, and the category, and there's also the image URL here. But if I click run now, that's actually just returning
the title and category. When I click run, I only get the data that I ask for. So that's what's super nice about GraphQL. Now, so yeah, you have
created a GraphQL API and a Next.js API route. You defined your schema. You also defined a resolver. And now what you can do is
just define more resolvers and define more queries and mutations and then define their
implementations and a resolver. However, so far, what we've done is we
returned static data, and now we'll take a look at
how to actually return data from the database using Prisma. So if we go back to our editor, what we need to do is first create an instance of Prisma client so that we can use it across our app. So create a new folder in the root folder and just call it lib. This will contain kinda like
just the Prisma instance. And whenever we want to
initialize kinda like something, so whenever we want to
initialize a certain library, we will do it inside this folder and then import it everywhere. So for example, we'll be using Apollo Client, and we need to create an
instance of Apollo Client. So we'll also create
it in this lib folder. So in this lib folder, create a new file and
just call it prisma.ts. So to set up Prisma
client in a Next.js app, you have to kinda use a certain workaround and that's because of how Next.js works. So Next.js fast refresh, and what happens is that
every time you hit save, it will create a new
instance of Prisma Client. Now, this will very quickly
exhaust the connection limit for your database, so that's why what we're
doing is we're first checking if we're in production. If we're in production, we'll just create a new
instance of Prisma Client. However, if we're not in production, so we're working locally, what will happen is we will
attach the Prisma instance to the global object. This way if, for example,
we hit save again, we don't reinitialize Prisma Client. We will just use the one that we attached to the global object. So that's what we're doing here. So if we're saying, if not global.prisma, then attach the Prisma
Client to the global object. And then what we're doing is saying prisma equals global.prisma, and then we're exporting it. So now what we can do is
inside the resolvers file, we can just import Prisma from, and then say dot dot
slash and then lib/prisma. And then instead of returning this array, what we will do is actually
return a Prisma query. So we can just say await prisma dot, and then you can actually
see auto completion. We want to say .link.findMany. And so now we need to
make this function async. And when we hit save, things should work. So our app is still running. If we go to our GraphQL playground here, when I run this query, I should be getting
data from the database. And we already know that the
database contains four links and one user, so right now we should be
able to see four links. So if I hit run, I see that I have four links. So now we just fetched
data from the database. Now, while this resolver works, it's actually missing a
couple of useful arguments. So the first one is called parent. I'm prefixing it with an
underscore because I won't use it. And it contains the return
value of this field's parent, but we're not using it right now. We'll be using it later. And then we have args, and this argument
contains all the variables that are passed to a query or mutation. So for example, if we want to define a query
that returns a link by its ID, to actually access the ID, we'll be able to access it
using this args argument. And it's an object. So we can just say args.id
and our Prisma query and whenever we want to pass the ID. And then finally we
have the third argument, and it's context. So context contains contextual information about a query or a mutation. So usually what we would do is
we would include information like who the logged in user is and database connections. So instead of accessing
Prisma by importing it and using it in the resolver, what we should do is
actually access it by saying context.prisma.link.findMany. However, we actually haven't
defined our own context yet, and let's do that right now. So let's just delete this. And in the GraphQL folder, create a new file and call it context.ts. In this file, what we first want to do is import PrismaClient from prisma/client and import the Prisma instance that we created in the lib folder. So just say import Prisma
from dot dot slash lib/prisma. And what we want is to export a type. So just say export type context. And we will say that it will have Prisma and it's of type PrismaClient. Now we want to create a function, so that will create a context for us, and we'll be passing it to Apollo Server. So let's just say export async function, call it createContext, and this will take a request and response, and it will return an
object containing Prisma. And what we want is to
actually add the type. So this will return a promise, and it's going to be of type context. And everything works. And now if we go to our API, so inside the pages/api/graphql.ts, and what we want to do
is import createContext from our context file. And then in the Apollo Server
instance that we're creating, just say context is equal createContext. And we don't actually need to call it. And yeah, now everything
should just work as expected and we should be able to return
all links from the database. We're not using args, so let's just prefix it with underscore. Now, technically we
can rename this context to ctx for short. And the app is still running and we're still in the GraphQL Playground. If we run example query, we should also get the same data. Nothing will change, and it works as expected.