-
Notifications
You must be signed in to change notification settings - Fork 27k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'canary' into update/adding-basePath
- Loading branch information
Showing
15 changed files
with
403 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__generated__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
generateDir: __generated__ | ||
schema: '**/*.graphqls' | ||
documents: '**/*.graphql' | ||
plugins: | ||
- typescript | ||
- typescript-operations | ||
- typescript-react-apollo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# GraphQL and TypeScript Example | ||
|
||
## Deploy your own | ||
|
||
Deploy the example using [ZEIT Now](https://zeit.co/now): | ||
|
||
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/next.js/tree/canary/examples/with-typescript-graphql) | ||
|
||
## How to use | ||
|
||
### Using `create-next-app` | ||
|
||
Execute [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example: | ||
|
||
```bash | ||
npx create-next-app --example with-typescript-graphql with-typescript-graphql-app | ||
# or | ||
yarn create next-app --example with-typescript-graphql with-typescript-graphql-app | ||
``` | ||
|
||
### Download manually | ||
|
||
Download the example: | ||
|
||
```bash | ||
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-typescript-graphql | ||
cd with-typescript-graphql | ||
``` | ||
|
||
Install it and run: | ||
|
||
```bash | ||
npm install | ||
npm run dev | ||
# or | ||
yarn | ||
yarn dev | ||
``` | ||
|
||
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)): | ||
|
||
```bash | ||
now | ||
``` | ||
|
||
## The idea behind the example | ||
|
||
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/zeit/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. | ||
|
||
```typescript jsx | ||
import { useNewsQuery } from './news.grpahql' | ||
|
||
const News: React.FC = () => { | ||
// Typed already️⚡️ | ||
const { data: { news } } = useNewsQuery() | ||
if (news) <div>{news.map(...)}</div> | ||
} | ||
``` | ||
|
||
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` point to the same files. | ||
|
||
Note: Do not be alarmed that you see two renders being executed. Apollo recursively traverses the React render tree looking for Apollo query components. When it has done that, it fetches all these queries and then passes the result to a cache. This cache is then used to render the data on the server side (another React render). | ||
https://www.apollographql.com/docs/react/api/react-ssr/#getdatafromtree |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const resolvers = { | ||
Query: { | ||
viewer(_parent, _args, _context, _info) { | ||
return { id: 1, name: 'John Smith', status: 'cached' } | ||
}, | ||
}, | ||
} | ||
|
||
export default resolvers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { makeExecutableSchema } from 'graphql-tools' | ||
import typeDefs from './type-defs.graphqls' | ||
import resolvers from './resolvers' | ||
|
||
const schema = makeExecutableSchema({ | ||
typeDefs, | ||
resolvers, | ||
}) | ||
|
||
export default schema |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
type User { | ||
id: ID! | ||
name: String! | ||
status: String! | ||
} | ||
|
||
type Query { | ||
viewer: User! | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
query Viewer { | ||
viewer { | ||
id | ||
name | ||
status | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { NextPage, NextPageContext } from 'next' | ||
import React from 'react' | ||
import Head from 'next/head' | ||
import { ApolloProvider } from '@apollo/react-hooks' | ||
import { ApolloClient } from 'apollo-client' | ||
import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory' | ||
|
||
type TApolloClient = ApolloClient<NormalizedCacheObject> | ||
|
||
type InitialProps = { | ||
apolloClient: TApolloClient | ||
apolloState: any | ||
} & Record<string, any> | ||
|
||
type WithApolloPageContext = { | ||
apolloClient: TApolloClient | ||
} & NextPageContext | ||
|
||
let globalApolloClient: TApolloClient | ||
|
||
/** | ||
* Creates and provides the apolloContext | ||
* to a next.js PageTree. Use it by wrapping | ||
* your PageComponent via HOC pattern. | ||
*/ | ||
export default function withApolo( | ||
PageComponent: NextPage, | ||
{ ssr = true } = {} | ||
) { | ||
const WithApollo = ({ | ||
apolloClient, | ||
apolloState, | ||
...pageProps | ||
}: InitialProps) => { | ||
const client = apolloClient || initApolloClient(apolloState) | ||
return ( | ||
<ApolloProvider client={client}> | ||
<PageComponent {...pageProps} /> | ||
</ApolloProvider> | ||
) | ||
} | ||
|
||
// Set the correct displayName in development | ||
if (process.env.NODE_ENV !== 'production') { | ||
const displayName = | ||
PageComponent.displayName || PageComponent.name || 'Component' | ||
|
||
if (displayName === 'App') { | ||
console.warn('This withApollo HOC only works with PageComponents.') | ||
} | ||
|
||
WithApollo.displayName = `withApollo(${displayName})` | ||
} | ||
|
||
if (ssr || PageComponent.getInitialProps) { | ||
WithApollo.getInitialProps = async (ctx: WithApolloPageContext) => { | ||
const { AppTree } = ctx | ||
|
||
// Initialize ApolloClient, add it to the ctx object so | ||
// we can use it in `PageComponent.getInitialProp`. | ||
const apolloClient = (ctx.apolloClient = initApolloClient()) | ||
|
||
// Run wrapped getInitialProps methods | ||
let pageProps = {} | ||
if (PageComponent.getInitialProps) { | ||
pageProps = await PageComponent.getInitialProps(ctx) | ||
} | ||
|
||
// Only on the server: | ||
if (typeof window === 'undefined') { | ||
// When redirecting, the response is finished. | ||
// No point in continuing to render | ||
if (ctx.res && ctx.res.finished) { | ||
return pageProps | ||
} | ||
|
||
// Only if ssr is enabled | ||
if (ssr) { | ||
try { | ||
// Run all GraphQL queries | ||
const { getDataFromTree } = await import('@apollo/react-ssr') | ||
await getDataFromTree( | ||
<AppTree | ||
pageProps={{ | ||
...pageProps, | ||
apolloClient, | ||
}} | ||
/> | ||
) | ||
} catch (error) { | ||
// Prevent Apollo Client GraphQL errors from crashing SSR. | ||
// Handle them in components via the data.error prop: | ||
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error | ||
console.error('Error while running `getDataFromTree`', error) | ||
} | ||
|
||
// getDataFromTree does not call componentWillUnmount | ||
// head side effect therefore need to be cleared manually | ||
Head.rewind() | ||
} | ||
} | ||
|
||
// Extract query data from the Apollo store | ||
const apolloState = apolloClient.cache.extract() | ||
|
||
return { | ||
...pageProps, | ||
apolloState, | ||
} | ||
} | ||
} | ||
|
||
return WithApollo | ||
} | ||
|
||
/** | ||
* Always creates a new apollo client on the server | ||
* Creates or reuses apollo client in the browser. | ||
* @param {Object} initialState | ||
*/ | ||
function initApolloClient(initialState?: any) { | ||
// Make sure to create a new client for every server-side request so that data | ||
// isn't shared between connections (which would be bad) | ||
if (typeof window === 'undefined') { | ||
return createApolloClient(initialState) | ||
} | ||
|
||
// Reuse client on the client-side | ||
if (!globalApolloClient) { | ||
globalApolloClient = createApolloClient(initialState) | ||
} | ||
|
||
return globalApolloClient | ||
} | ||
|
||
/** | ||
* Creates and configures the ApolloClient | ||
* @param {Object} [initialState={}] | ||
*/ | ||
function createApolloClient(initialState = {}) { | ||
const ssrMode = typeof window === 'undefined' | ||
const cache = new InMemoryCache().restore(initialState) | ||
|
||
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient | ||
return new ApolloClient({ | ||
ssrMode, | ||
link: createIsomorphLink(), | ||
cache, | ||
}) | ||
} | ||
|
||
function createIsomorphLink() { | ||
if (typeof window === 'undefined') { | ||
const { SchemaLink } = require('apollo-link-schema') | ||
const schema = require('./schema').default | ||
return new SchemaLink({ schema }) | ||
} else { | ||
const { HttpLink } = require('apollo-link-http') | ||
return new HttpLink({ | ||
uri: '/api/graphql', | ||
credentials: 'same-origin', | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/// <reference types="next" /> | ||
/// <reference types="next/types/global" /> | ||
|
||
declare module '*.graphqls' { | ||
import { DocumentNode } from 'graphql' | ||
export default typeof DocumentNode | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
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/, | ||
loader: 'graphql-tag/loader', | ||
}) | ||
|
||
return config | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "with-typescript-graphql", | ||
"version": "0.1.0", | ||
"author": "", | ||
"license": "ISC", | ||
"scripts": { | ||
"codegen": "graphql-let", | ||
"dev": "yarn codegen && next", | ||
"build": "yarn codegen && next build", | ||
"start": "next start" | ||
}, | ||
"dependencies": { | ||
"@apollo/react-common": "3.1.3", | ||
"@apollo/react-components": "^3.1.3", | ||
"@apollo/react-hooks": "3.1.3", | ||
"@apollo/react-ssr": "3.1.3", | ||
"apollo-cache-inmemory": "1.6.5", | ||
"apollo-client": "2.6.8", | ||
"apollo-link-http": "1.5.16", | ||
"apollo-link-schema": "1.2.4", | ||
"apollo-server-micro": "2.9.14", | ||
"apollo-utilities": "^1.3.3", | ||
"graphql": "^14.5.8", | ||
"graphql-tag": "^2.10.1", | ||
"next": "latest", | ||
"react": "^16.12.0", | ||
"react-dom": "^16.12.0" | ||
}, | ||
"devDependencies": { | ||
"@graphql-codegen/typescript": "^1.9.1", | ||
"@graphql-codegen/typescript-operations": "^1.9.1", | ||
"@graphql-codegen/typescript-react-apollo": "^1.9.1", | ||
"@types/react": "^16.9.17", | ||
"graphql-let": "0.x", | ||
"typescript": "^3.7.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import Link from 'next/link' | ||
|
||
export default () => ( | ||
<div> | ||
This is a static page goto{' '} | ||
<Link href="/"> | ||
<a>dynamic</a> | ||
</Link>{' '} | ||
page. | ||
</div> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ApolloServer } from 'apollo-server-micro' | ||
import schema from '../../lib/schema' | ||
|
||
const apolloServer = new ApolloServer({ schema }) | ||
|
||
export const config = { | ||
api: { | ||
bodyParser: false, | ||
}, | ||
} | ||
|
||
export default apolloServer.createHandler({ path: '/api/graphql' }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import withApollo from '../lib/with-apollo' | ||
import Link from 'next/link' | ||
import { useViewerQuery } from '../lib/viewer.graphql' | ||
|
||
const Index = () => { | ||
const { data } = useViewerQuery() | ||
|
||
if (data) { | ||
const { viewer } = data | ||
return ( | ||
<div> | ||
You're signed in as {viewer.name} and you're {viewer.status} goto{' '} | ||
<Link href="/about"> | ||
<a>static</a> | ||
</Link>{' '} | ||
page. | ||
</div> | ||
) | ||
} | ||
|
||
return <div>...</div> | ||
} | ||
|
||
export default withApollo(Index) |
Oops, something went wrong.