Build a Next App with a full GraphQL API using just a JSON or CSV file
Ali Raslan
Posted on January 24, 2023
Introduction
In this guide we'll be using a JSON or CSV file with some data in it to build a fully featured GraphQL API inside a NextJS application.
For this to work, we'll need some tools, here's links to all the tools used in this guide if you'd like to check any of them out or potentially explore replacements:
- Prisma The database connector where all the magic happens
- Typegraphql-Prisma To generate the TypeGraphQL schema for Apollo server from Prisma
- TypeGraphQL
- Apollo Server
- graphql-code-generator To build the types for the frontend
- Next The fullstack framework we'll be using
- React The frontend part
Create a MongoDB database and add your data (skip if you already have a MongoDB database with data in it)
Head to mongodb.com and sign up for a free Atlas account or set up mongodb locally
Create your project
Create a cluster, select SHARED
for the free tier.
Create your user and add your IP address to the list of allowed IP addresses (or 0.0.0.0 to allow any connection).
Press connect
on your cluster on mongodb.com
Click on Compass
Download Compass if you don't have it, and copy the connection string
Paste the connection string into Compass, replacing with the password for the database user you created earlier and press connect
In the database name and collection name fields, describe the data type your database will contain, for example a database of football players could be called football_players
or players
Once the database is created, click on it
You can now upload your JSON or CSV file to the database to fill it
We now have a database with the data we need, you can close Compass.
Create a NextJS app with TypeScript
Answer the questions as you like but when asked
'if you want to use the new /app
directory' no
'if you want to use src
directory' no
yarn create next-app --typescript
Install dependencies
yarn add prisma typegraphql-prisma -D
yarn add @apollo/server @as-integrations/next @prisma/client graphql class-validator type-graphql@next reflect-metadata graphql-scalars graphql-fields @types/graphql-fields tslib
Initialize prisma
yarn prisma init --datasource-provider mongodb
Add mongodb connection and the eventual graphql api url to .env
DATABASE_URL="mongodb+srv://username:password@address.mongodb.net/tablename?retryWrites=true&w=majority"
NEXT_PUBLIC_API_URL="http://localhost:3000/api/graphql"
Pull the database schema into Prisma
yarn prisma db pull
Add generator to prisma
generator typegraphql {
provider = "typegraphql-prisma"
output = "../prisma/generated/type-graphql"
simpleResolvers = true
}
Create tsconfig.json or modify it at the NextJS root
{
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
},
"sourceMap": true,
"outDir": "dist",
"strict": true,
"lib": ["esnext", "esnext.asynciterable", "dom", "dom.iterable"],
"target": "es2018",
"module": "commonjs",
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"skipLibCheck": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
Generate the prisma schema
yarn prisma generate
Create /pages/api/graphql.ts
import 'reflect-metadata';
import { resolvers } from 'prisma/generated/type-graphql';
import { PrismaClient } from '@prisma/client';
import { ApolloServer } from '@apollo/server';
import * as tq from 'type-graphql';
import { startServerAndCreateNextHandler } from '@as-integrations/next';
const prisma = new PrismaClient();
const schema = tq.buildSchemaSync({
resolvers,
validate: false,
});
const server = new ApolloServer({ schema });
export default startServerAndCreateNextHandler(server, {
context: async () => ({ prisma }),
});
Add this line to the top of _app.tsx
import 'reflect-metadata';
Start your Next app
yarn dev
Access the server at http://localhost:3000/api/graphql
Enabling CORS to use outside Vercel
Add the following to next.config.js
.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
async headers() {
return [
{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{
key: 'Access-Control-Allow-Methods',
value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT',
},
{
key: 'Access-Control-Allow-Headers',
value:
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version',
},
],
},
];
},
};
module.exports = nextConfig;
Automatically generate types for the client
Install graphql-code-generator
yarn add -D ts-node @graphql-codegen/cli @graphql-codegen/client-preset
Create codegen.ts
import { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: "http://localhost:3000/api/graphql",
documents: ['pages/**/*.tsx'],
ignoreNoDocuments: true, // for better experience with the watcher
generates: {
'./gql/': {
preset: 'client',
plugins: [],
},
},
};
export default config;
Install concurrently
yarn add -D concurrently
Modify your dev
script in package.json
"dev": "concurrently \"yarn next dev\" \"yarn graphql-codegen --watch\"",
Communicating with the API within Next
Add graphql-request and react-query
yarn add graphql-request react-query
Add React query provider and a global graphql-request client to _app.tsx
import 'reflect-metadata';
import type { AppProps } from 'next/app';
import { QueryClient, QueryClientProvider } from 'react-query';
import { GraphQLClient } from 'graphql-request';
const queryClient = new QueryClient();
export const client = new GraphQLClient(process.env.NEXT_PUBLIC_API_URL as string, {
headers: {},
});
export default function App({ Component, pageProps }: AppProps) {
return (
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
</QueryClientProvider>
);
}
You're now ready to use react-query with your types, here's an example index.tsx
with a database of players
import Head from 'next/head';
import { graphql } from 'gql';
import { useQuery } from 'react-query';
import React from 'react';
import { client } from './_app';
const findManyPlayers = graphql(`
query FindManyPlayers($take: Int) {
findManyPlayers(take: $take) {
age
hits
name
id
nationality
overall
player_id
position
potential
team
}
}
`);
export default function Home() {
const { data } = useQuery(['players'], async () =>
client.request(findManyPlayers, {
take: 5,
})
);
if (!data) return <></>;
return (
<>
<Head>
<title>Create Next App</title>
<meta name='description' content='Generated by create next app' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' href='/favicon.ico' />
</Head>
<main>
<div>
{data.findManyPlayers.map((player) => (
<React.Fragment key={player.id}>
<p>{player.name}</p>
</React.Fragment>
))}
</div>
</main>
</>
);
}
Posted on January 24, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.