Next.js GraphQL API using Apollo Server 3 and Prisma

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
- 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.
Info
Channel: Prisma
Views: 3,745
Rating: undefined out of 5
Keywords: typescript, nodejs, orms, database, graphql, api, fullstack, next.js, developer, development, app building, apollo, server
Id: RJpevpbC4YY
Channel Id: undefined
Length: 21min 30sec (1290 seconds)
Published: Fri Oct 01 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.