diff --git a/examples/with-typescript-graphql/.babelrc b/examples/with-typescript-graphql/.babelrc deleted file mode 100644 index 3f90997510587..0000000000000 --- a/examples/with-typescript-graphql/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": ["graphql-let/babel"] -} diff --git a/examples/with-typescript-graphql/.gitignore b/examples/with-typescript-graphql/.gitignore index bb4ca946e5e9d..aa06bce141350 100644 --- a/examples/with-typescript-graphql/.gitignore +++ b/examples/with-typescript-graphql/.gitignore @@ -38,3 +38,6 @@ yarn-error.log* *.graphql.d.ts *.graphqls.d.ts .cache + +lib/resolvers-types.ts +lib/graphql-operations.ts diff --git a/examples/with-typescript-graphql/.graphql-let.yml b/examples/with-typescript-graphql/.graphql-let.yml deleted file mode 100644 index 223562044eaf6..0000000000000 --- a/examples/with-typescript-graphql/.graphql-let.yml +++ /dev/null @@ -1,6 +0,0 @@ -schema: '**/*.graphqls' -documents: '**/*.graphql' -plugins: - - typescript-operations - - typescript-react-apollo -cacheDir: .cache diff --git a/examples/with-typescript-graphql/README.md b/examples/with-typescript-graphql/README.md index c7f4eb4b35117..3b50d7d8fdc0f 100644 --- a/examples/with-typescript-graphql/README.md +++ b/examples/with-typescript-graphql/README.md @@ -2,21 +2,22 @@ One of the strengths of GraphQL is [enforcing data types on runtime](https://graphql.github.io/graphql-spec/June2018/#sec-Value-Completion). Further, TypeScript and [GraphQL Code Generator](https://graphql-code-generator.com/) (graphql-codegen) make it safer by typing data statically, so you can write truly type-protected code with rich IDE assists. -This template extends [Apollo Server and Client Example](https://github.com/vercel/next.js/tree/canary/examples/api-routes-apollo-server-and-client#readme) by rewriting in TypeScript and integrating [graphql-let](https://github.com/piglovesyou/graphql-let#readme), which runs [TypeScript React Apollo](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo) in [graphql-codegen](https://github.com/dotansimha/graphql-code-generator#readme) under the hood. It enhances the typed GraphQL use as below: +This template gives you the best start to use GraphQL with fully typed queries (client-side) and resolvers (server-side), all this with minimum bundle size 📦 ```tsx -import { useNewsQuery } from './news.graphql' +import { useQuery } from '@apollo/client' +import { ViewerDocument } from 'lib/graphql-operations' const News = () => { - // Typed already️⚡️ - const { data: { news } } = useNewsQuery() + // Typed already️⚡️ + const { + data: { viewer }, + } = useQuery(ViewerDocument) - return
{news.map(...)}
+ return
{viewer.name}
} ``` -By default `**/*.graphqls` is recognized as GraphQL schema and `**/*.graphql` as GraphQL documents. If you prefer the other extensions, make sure the settings of the webpack loader in `next.config.js` and `.graphql-let.yml` are consistent. - ## Deploy your own Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-typescript-graphql) diff --git a/examples/with-typescript-graphql/codegen.yml b/examples/with-typescript-graphql/codegen.yml new file mode 100644 index 0000000000000..f53d625d190eb --- /dev/null +++ b/examples/with-typescript-graphql/codegen.yml @@ -0,0 +1,14 @@ +schema: + - 'lib/schema.ts': + noRequire: true +documents: ./lib/documents/*.graphql +generates: + ./lib/graphql-operations.ts: + plugins: + - typescript + - typescript-operations + - typed-document-node + ./lib/resolvers-types.ts: + plugins: + - typescript + - typescript-resolvers diff --git a/examples/with-typescript-graphql/graphql.d.ts b/examples/with-typescript-graphql/graphql.d.ts deleted file mode 100644 index d0cab7295668b..0000000000000 --- a/examples/with-typescript-graphql/graphql.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.graphqls' { - import { DocumentNode } from 'graphql' - export default typeof DocumentNode -} - -declare module '*.yml' diff --git a/examples/with-typescript-graphql/lib/apollo.ts b/examples/with-typescript-graphql/lib/apollo.ts index 961769f6491e7..41135d3554b6d 100644 --- a/examples/with-typescript-graphql/lib/apollo.ts +++ b/examples/with-typescript-graphql/lib/apollo.ts @@ -5,6 +5,8 @@ import { InMemoryCache, NormalizedCacheObject, } from '@apollo/client' +import resolvers from './resolvers' +import typeDefs from './schema' let apolloClient: ApolloClient | undefined @@ -16,7 +18,12 @@ export type ResolverContext = { function createIsomorphLink(context: ResolverContext = {}) { if (typeof window === 'undefined') { const { SchemaLink } = require('@apollo/client/link/schema') - const { schema } = require('./schema') + const { makeExecutableSchema } = require('@graphql-tools/schema') + + const schema = makeExecutableSchema({ + typeDefs, + resolvers, + }) return new SchemaLink({ schema, context }) } else { const { HttpLink } = require('@apollo/client') diff --git a/examples/with-typescript-graphql/lib/documents/updateName.graphql b/examples/with-typescript-graphql/lib/documents/updateName.graphql new file mode 100644 index 0000000000000..8c457c478d057 --- /dev/null +++ b/examples/with-typescript-graphql/lib/documents/updateName.graphql @@ -0,0 +1,7 @@ +mutation UpdateName($name: String!) { + updateName(name: $name) { + id + name + status + } +} diff --git a/examples/with-typescript-graphql/lib/documents/viewer.graphql b/examples/with-typescript-graphql/lib/documents/viewer.graphql new file mode 100644 index 0000000000000..5b4bc2effea0a --- /dev/null +++ b/examples/with-typescript-graphql/lib/documents/viewer.graphql @@ -0,0 +1,11 @@ +query Viewer { + viewer { + ...Partial + status + } +} + +fragment Partial on User { + id + name +} diff --git a/examples/with-typescript-graphql/lib/partial.graphql b/examples/with-typescript-graphql/lib/partial.graphql deleted file mode 100644 index 7ad627852a9c8..0000000000000 --- a/examples/with-typescript-graphql/lib/partial.graphql +++ /dev/null @@ -1,4 +0,0 @@ -fragment Partial on User { - id - name -} diff --git a/examples/with-typescript-graphql/lib/resolvers.ts b/examples/with-typescript-graphql/lib/resolvers.ts index e2125756018c6..8d2633d7ed5d6 100644 --- a/examples/with-typescript-graphql/lib/resolvers.ts +++ b/examples/with-typescript-graphql/lib/resolvers.ts @@ -1,5 +1,4 @@ -import { QueryResolvers, MutationResolvers } from '@graphql-types@' -import { ResolverContext } from './apollo' +import { Resolvers } from './resolvers-types' const userProfile = { id: String(1), @@ -7,17 +6,18 @@ const userProfile = { status: 'cached', } -const Query: Required> = { - viewer(_parent, _args, _context, _info) { - return userProfile +const resolvers: Resolvers = { + Query: { + viewer(_parent, _args, _context, _info) { + return userProfile + }, }, -} - -const Mutation: Required> = { - updateName(_parent, _args, _context, _info) { - userProfile.name = _args.name - return userProfile + Mutation: { + updateName(_parent, _args, _context, _info) { + userProfile.name = _args.name + return userProfile + }, }, } -export default { Query, Mutation } +export default resolvers diff --git a/examples/with-typescript-graphql/lib/schema.ts b/examples/with-typescript-graphql/lib/schema.ts index b7e0f139e062e..50682436e34ae 100644 --- a/examples/with-typescript-graphql/lib/schema.ts +++ b/examples/with-typescript-graphql/lib/schema.ts @@ -1,14 +1,17 @@ -import { join } from 'path' -import { makeExecutableSchema } from '@graphql-tools/schema' -import { loadFilesSync } from '@graphql-tools/load-files' -import { mergeTypeDefs } from '@graphql-tools/merge' -import graphQLLetConfig from '../.graphql-let.yml' -import resolvers from './resolvers' +const typeDefs = /* GraphQL */ ` + type User { + id: ID! + name: String! + status: String! + } -const loadedFiles = loadFilesSync(join(process.cwd(), graphQLLetConfig.schema)) -const typeDefs = mergeTypeDefs(loadedFiles) + type Query { + viewer: User! + } -export const schema = makeExecutableSchema({ - typeDefs, - resolvers, -}) + type Mutation { + updateName(name: String!): User! + } +` + +export default typeDefs diff --git a/examples/with-typescript-graphql/lib/type-defs.graphqls b/examples/with-typescript-graphql/lib/type-defs.graphqls deleted file mode 100644 index 0b86a615950ea..0000000000000 --- a/examples/with-typescript-graphql/lib/type-defs.graphqls +++ /dev/null @@ -1,7 +0,0 @@ -type Query { - viewer: User! -} - -type Mutation { - updateName(name: String!): User! -} diff --git a/examples/with-typescript-graphql/lib/user.graphqls b/examples/with-typescript-graphql/lib/user.graphqls deleted file mode 100644 index b9aad32a51ef5..0000000000000 --- a/examples/with-typescript-graphql/lib/user.graphqls +++ /dev/null @@ -1,5 +0,0 @@ -type User { - id: ID! - name: String! - status: String! -} diff --git a/examples/with-typescript-graphql/lib/viewer.graphql b/examples/with-typescript-graphql/lib/viewer.graphql deleted file mode 100644 index 9be08356d8c92..0000000000000 --- a/examples/with-typescript-graphql/lib/viewer.graphql +++ /dev/null @@ -1,16 +0,0 @@ -# import Partial from './partial.graphql' - -query Viewer { - viewer { - ...Partial - status - } -} - -mutation UpdateName($name: String!) { - updateName(name: $name) { - id - name - status - } -} diff --git a/examples/with-typescript-graphql/next.config.js b/examples/with-typescript-graphql/next.config.js index c6d3173c08ece..6285426ed3f78 100644 --- a/examples/with-typescript-graphql/next.config.js +++ b/examples/with-typescript-graphql/next.config.js @@ -1,17 +1,5 @@ module.exports = { webpack(config, options) { - config.module.rules.push({ - test: /\.graphql$/, - exclude: /node_modules/, - use: [options.defaultLoaders.babel, { loader: 'graphql-let/loader' }], - }) - - config.module.rules.push({ - test: /\.graphqls$/, - exclude: /node_modules/, - use: ['graphql-let/schema/loader'], - }) - config.module.rules.push({ test: /\.ya?ml$/, type: 'json', diff --git a/examples/with-typescript-graphql/package.json b/examples/with-typescript-graphql/package.json index 796b595e1a704..346f287a477be 100644 --- a/examples/with-typescript-graphql/package.json +++ b/examples/with-typescript-graphql/package.json @@ -1,42 +1,40 @@ { "private": true, "scripts": { - "codegen": "graphql-let", + "codegen": "graphql-codegen -r ts-node/register", "dev": "yarn codegen && next", "build": "yarn codegen && next build", "test": "yarn codegen && jest", "start": "next start" }, "dependencies": { - "@apollo/client": "^3.1.3", + "@apollo/client": "^3.5.10", + "graphql-tag": "^2.12.6", "@graphql-tools/load-files": "6.0.18", + "@graphql-yoga/node": "^2.2.1", "@graphql-tools/merge": "6.0.18", "@graphql-tools/schema": "6.0.18", - "apollo-server-micro": "^2.16.1", - "graphql": "15.3.0", - "next": "latest", + "graphql": "15.6.0", + "next": "^12.1.5", "react": "^17.0.2", "react-dom": "^17.0.2" }, "devDependencies": { - "@graphql-codegen/cli": "^2.2.1", - "@graphql-codegen/import-types-preset": "^2.1.6", - "@graphql-codegen/plugin-helpers": "^2.2.0", - "@graphql-codegen/typescript": "^2.2.4", - "@graphql-codegen/typescript-operations": "^2.1.8", - "@graphql-codegen/typescript-react-apollo": "^3.1.6", - "@graphql-codegen/typescript-resolvers": "^2.3.2", + "@graphql-codegen/typed-document-node": "^2.2.8", + "@graphql-codegen/cli": "^2.6.2", + "@graphql-codegen/typescript": "^2.4.8", + "@graphql-codegen/typescript-operations": "^2.3.5", + "@graphql-codegen/typescript-resolvers": "^2.6.1", + "@types/jest": "^27.0.2", + "@types/mocha": "^9.0.0", "@types/react": "^16.9.46", "@types/react-dom": "^16.9.8", "@types/react-test-renderer": "^17.0.1", - "@types/jest": "^27.0.2", - "@types/mocha": "^9.0.0", "babel-jest": "27.2.5", - "graphql-let": "^0.18.6", - "graphql-tag": "2.11.0", "jest": "^27.2.5", "react-test-renderer": "^17.0.1", - "typescript": "^4.5.4", - "yaml-loader": "0.6.0" + "typescript": "^4.6.3", + "yaml-loader": "0.6.0", + "ts-node": "10.8.0" } } diff --git a/examples/with-typescript-graphql/pages/api/graphql.ts b/examples/with-typescript-graphql/pages/api/graphql.ts index 2018e87034117..1228b87ab5585 100644 --- a/examples/with-typescript-graphql/pages/api/graphql.ts +++ b/examples/with-typescript-graphql/pages/api/graphql.ts @@ -1,12 +1,16 @@ -import { ApolloServer } from 'apollo-server-micro' -import { schema } from '../../lib/schema' +import { createServer } from '@graphql-yoga/node' +import gql from 'graphql-tag' -const apolloServer = new ApolloServer({ schema }) +import resolvers from 'lib/resolvers' +import typeDefs from 'lib/schema' -export const config = { - api: { - bodyParser: false, +const server = createServer({ + schema: { + typeDefs: gql(typeDefs), + resolvers, }, -} + endpoint: '/api/graphql', + // graphiql: false // uncomment to disable GraphiQL +}) -export default apolloServer.createHandler({ path: '/api/graphql' }) +export default server diff --git a/examples/with-typescript-graphql/pages/index.tsx b/examples/with-typescript-graphql/pages/index.tsx index cae3acee2b8c1..f9a2de3b04aed 100644 --- a/examples/with-typescript-graphql/pages/index.tsx +++ b/examples/with-typescript-graphql/pages/index.tsx @@ -1,42 +1,45 @@ +import { useMutation, useQuery } from '@apollo/client' +import { UpdateNameDocument, ViewerDocument } from 'lib/graphql-operations' import Link from 'next/link' import { useState } from 'react' -import { - ViewerQuery, - useViewerQuery, - useUpdateNameMutation, - ViewerDocument, -} from '../lib/viewer.graphql' import { initializeApollo } from '../lib/apollo' const Index = () => { - const { viewer } = useViewerQuery().data! + const { data } = useQuery(ViewerDocument) const [newName, setNewName] = useState('') - const [updateNameMutation] = useUpdateNameMutation() + const [updateNameMutation] = useMutation(UpdateNameDocument) const onChangeName = () => { updateNameMutation({ variables: { name: newName, }, - //Follow apollo suggestion to update cache - //https://www.apollographql.com/docs/angular/features/cache-updates/#update + // Follow apollo suggestion to update cache + // https://www.apollographql.com/docs/angular/features/cache-updates/#update update: (cache, mutationResult) => { const { data } = mutationResult if (!data) return // Cancel updating name in cache if no data is returned from mutation. // Read the data from our cache for this query. - const { viewer } = cache.readQuery({ + const result = cache.readQuery({ query: ViewerDocument, - }) as ViewerQuery - const newViewer = { ...viewer } + }) + const newViewer = result ? { ...result.viewer } : null // Add our comment from the mutation to the end. - newViewer.name = data.updateName.name // Write our data back to the cache. - cache.writeQuery({ query: ViewerDocument, data: { viewer: newViewer } }) + if (newViewer) { + newViewer.name = data.updateName.name + cache.writeQuery({ + query: ViewerDocument, + data: { viewer: newViewer }, + }) + } }, }) } - return ( + const viewer = data?.viewer + + return viewer ? (
You're signed in as {viewer.name} and you're {viewer.status}. Go to the{' '} @@ -52,7 +55,7 @@ const Index = () => {
- ) + ) : null } export async function getStaticProps() { diff --git a/examples/with-typescript-graphql/tsconfig.json b/examples/with-typescript-graphql/tsconfig.json index 56bcf4a2f3105..938107c1923e2 100644 --- a/examples/with-typescript-graphql/tsconfig.json +++ b/examples/with-typescript-graphql/tsconfig.json @@ -14,11 +14,6 @@ "isolatedModules": true, "jsx": "preserve", "baseUrl": ".", - "paths": { - "@graphql-types@": [ - "node_modules/@types/graphql-let/__generated__/__types__" - ] - }, "incremental": true }, "include": ["next-env.d.ts", "graphql.d.ts", "**/*.ts", "**/*.tsx"],