장재혁

Link front and back

Showing 39 changed files with 484 additions and 567 deletions
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
app.useGlobalPipes(new ValidationPipe())
await app.listen(5000)
}
bootstrap();
bootstrap()
......
......@@ -10,6 +10,7 @@
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
"incremental": true,
"skipLibCheck": true
}
}
......
......@@ -2,9 +2,9 @@ overwrite: true
require:
- ts-node/register
generates:
./types/api-generated-types.ts:
./src/types/api-generated-types.ts:
schema: "src/graphql/api/api.graphql"
# documents: "src/graphql/api/**/*.graphql"
# documents: "src/graphql/api/*.graphql"
plugins:
- "typescript"
# - 'fragment-matcher'
......
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./types/api-generated-types"), exports);
export declare type Maybe<T> = T | null;
export declare type Exact<T extends {
[key: string]: unknown;
}> = {
[K in keyof T]: T[K];
};
export declare type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]?: Maybe<T[SubKey]>;
};
export declare type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>;
};
/** All built-in and custom scalars, mapped to their actual values */
export declare type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
};
export declare type Comment = {
__typename?: 'Comment';
author: Scalars['String'];
content: Scalars['String'];
created_date: Scalars['String'];
id: Scalars['Int'];
parent: Scalars['Int'];
post_id: Scalars['Int'];
updated_date?: Maybe<Scalars['String']>;
};
export declare type CreateCommentInput = {
content: Scalars['String'];
parent?: Maybe<Scalars['Float']>;
post_id: Scalars['Int'];
};
export declare type CreateLikeableInput = {
like_dislike: Scalars['Int'];
likeable_id: Scalars['Int'];
likeable_type: Scalars['String'];
};
export declare type CreateMyInput = {
name: Scalars['String'];
type?: Maybe<Scalars['String']>;
};
export declare type CreatePostInput = {
category: Scalars['String'];
content: Scalars['String'];
title: Scalars['String'];
};
export declare type GetCommentInput = {
author?: Maybe<Scalars['String']>;
parent?: Maybe<Scalars['String']>;
post_id?: Maybe<Scalars['Float']>;
};
export declare type GetLikeableInput = {
likeable_id?: Maybe<Scalars['Float']>;
likeable_type: Scalars['String'];
user_id?: Maybe<Scalars['String']>;
};
export declare type GetPostInput = {
author?: Maybe<Scalars['String']>;
category?: Maybe<Scalars['String']>;
id?: Maybe<Scalars['Float']>;
};
export declare type Likeable = {
__typename?: 'Likeable';
created_date: Scalars['String'];
id: Scalars['Int'];
like_dislike: Scalars['Int'];
likeable_id: Scalars['Int'];
likeable_type: Scalars['String'];
user_id: Scalars['String'];
};
export declare type Mutation = {
__typename?: 'Mutation';
createComment: Comment;
createLikeable: Likeable;
createMyPage: MyPage;
createPost: Post;
};
export declare type MutationcreateCommentArgs = {
input: CreateCommentInput;
};
export declare type MutationcreateLikeableArgs = {
input: CreateLikeableInput;
};
export declare type MutationcreateMyPageArgs = {
createMyInput: CreateMyInput;
};
export declare type MutationcreatePostArgs = {
input: CreatePostInput;
};
export declare type MyPage = {
__typename?: 'MyPage';
id: Scalars['Int'];
name: Scalars['String'];
type?: Maybe<Scalars['String']>;
};
export declare type Post = {
__typename?: 'Post';
author: Scalars['String'];
category: Scalars['String'];
content: Scalars['String'];
created_date: Scalars['String'];
id: Scalars['Int'];
title: Scalars['String'];
updated_date?: Maybe<Scalars['String']>;
};
export declare type Query = {
__typename?: 'Query';
getAllComments: Array<Comment>;
getAllLikes: Array<Likeable>;
getAllPosts: Array<Post>;
getComment: Comment;
getLikeable: Likeable;
getPost: Post;
getSomeComments: Array<Comment>;
getSomePosts: Array<Post>;
getTotalLikes: Scalars['Float'];
myPage: Array<MyPage>;
};
export declare type QuerygetCommentArgs = {
id: Scalars['Float'];
};
export declare type QuerygetLikeableArgs = {
id: Scalars['Float'];
};
export declare type QuerygetPostArgs = {
id: Scalars['Float'];
};
export declare type QuerygetSomeCommentsArgs = {
input: GetCommentInput;
};
export declare type QuerygetSomePostsArgs = {
input: GetPostInput;
};
export declare type QuerygetTotalLikesArgs = {
input: GetLikeableInput;
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
{
"name": "@graphql-community/shared",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start",
"build": "rimraf dist && tsc --build",
"typegen" : "graphql-codegen --config codegen.yml"
},
"dependencies": {
......
export * from "./types/api-generated-types";
{
"compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "es2017"],
"module": "commonjs",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"declaration": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "es2017",
"outDir" : "./dist",
"rootDir": "./src"
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx"]
}
\ No newline at end of file
{
"presets": ["next/babel"]
}
......@@ -32,3 +32,8 @@ yarn-error.log*
# vercel
.vercel
# graphql-let
__generated__
*.graphql.d.ts
*.graphqls.d.ts
......
schema: '**/*.graphqls'
schemaEntrypoint: 'lib/type-defs.graphqls'
documents: '**/*.graphql'
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
cacheDir: __generated__
# Apollo Example
# TypeScript and GraphQL Example
[Apollo](https://www.apollographql.com/client/) is a GraphQL client that allows you to easily query the exact data you need from a GraphQL server. In addition to fetching and mutating data, Apollo analyzes your queries and their results to construct a client-side cache of your data, which is kept up to date as further queries and mutations are run.
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.
In this simple example, we integrate Apollo seamlessly with [Next.js data fetching methods](https://nextjs.org/docs/basic-features/data-fetching) to fetch queries in the server and hydrate them in the browser.
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 example relies on [Prisma + Nexus](https://github.com/prisma-labs/nextjs-graphql-api-examples) for its GraphQL backend.
```tsx
import { useNewsQuery } from './news.graphql'
## Demo
const News = () => {
// Typed already️⚡️
const { data: { news } } = useNewsQuery()
[https://next-with-apollo.now.sh](https://next-with-apollo.now.sh)
return <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` are consistent.
## Deploy your own
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-apollo&project-name=with-apollo&repository-name=with-apollo)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-typescript-graphql&project-name=with-typescript-graphql&repository-name=with-typescript-graphql)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
```bash
npx create-next-app --example with-apollo with-apollo-app
npx create-next-app --example with-typescript-graphql with-typescript-graphql-app
# or
yarn create next-app --example with-apollo with-apollo-app
yarn create next-app --example with-typescript-graphql with-typescript-graphql-app
```
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
......
export default function App({ children }) {
return (
<main>
{children}
<style jsx global>{`
* {
font-family: Menlo, Monaco, 'Lucida Console', 'Liberation Mono',
'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New',
monospace, serif;
}
body {
margin: 0;
padding: 25px 50px;
}
a {
color: #22bad9;
}
p {
font-size: 14px;
line-height: 24px;
}
article {
margin: 0 auto;
max-width: 650px;
}
button {
align-items: center;
background-color: #22bad9;
border: 0;
color: white;
display: flex;
padding: 5px 7px;
transition: background-color 0.3s;
}
button:active {
background-color: #1b9db7;
}
button:disabled {
background-color: #b5bebf;
}
button:focus {
outline: none;
}
`}</style>
</main>
)
}
export default function ErrorMessage({ message }) {
return (
<aside>
{message}
<style jsx>{`
aside {
padding: 1.5em;
font-size: 14px;
color: white;
background-color: red;
}
`}</style>
</aside>
)
}
import { useRouter } from 'next/router'
import Link from 'next/link'
export default function Header() {
const { pathname } = useRouter()
return (
<header>
<Link href="/">
<a className={pathname === '/' ? 'is-active' : ''}>Home</a>
</Link>
<Link href="/about">
<a className={pathname === '/about' ? 'is-active' : ''}>About</a>
</Link>
<Link href="/client-only">
<a className={pathname === '/client-only' ? 'is-active' : ''}>
Client-Only
</a>
</Link>
<Link href="/ssr">
<a className={pathname === '/ssr' ? 'is-active' : ''}>SSR</a>
</Link>
<style jsx>{`
header {
margin-bottom: 25px;
}
a {
font-size: 14px;
margin-right: 15px;
text-decoration: none;
}
.is-active {
text-decoration: underline;
}
`}</style>
</header>
)
}
const InfoBox = ({ children }) => (
<div className="info">
<style jsx>{`
.info {
margin-top: 20px;
margin-bottom: 20px;
padding-top: 20px;
padding-bottom: 20px;
border-top: 1px solid #ececec;
border-bottom: 1px solid #ececec;
}
`}</style>
{children}
</div>
)
export default InfoBox
import { gql, useQuery, NetworkStatus } from '@apollo/client'
import ErrorMessage from './ErrorMessage'
import PostUpvoter from './PostUpvoter'
export const ALL_POSTS_QUERY = gql`
query allPosts($first: Int!, $skip: Int!) {
allPosts(orderBy: { createdAt: desc }, first: $first, skip: $skip) {
id
title
votes
url
createdAt
}
_allPostsMeta {
count
}
}
`
export const allPostsQueryVars = {
skip: 0,
first: 10,
}
export default function PostList() {
const { loading, error, data, fetchMore, networkStatus } = useQuery(
ALL_POSTS_QUERY,
{
variables: allPostsQueryVars,
// Setting this value to true will make the component rerender when
// the "networkStatus" changes, so we are able to know if it is fetching
// more data
notifyOnNetworkStatusChange: true,
}
)
const loadingMorePosts = networkStatus === NetworkStatus.fetchMore
const loadMorePosts = () => {
fetchMore({
variables: {
skip: allPosts.length,
},
})
}
if (error) return <ErrorMessage message="Error loading posts." />
if (loading && !loadingMorePosts) return <div>Loading</div>
const { allPosts, _allPostsMeta } = data
const areMorePosts = allPosts.length < _allPostsMeta.count
return (
<section>
<ul>
{allPosts.map((post, index) => (
<li key={post.id}>
<div>
<span>{index + 1}. </span>
<a href={post.url}>{post.title}</a>
<PostUpvoter id={post.id} votes={post.votes} />
</div>
</li>
))}
</ul>
{areMorePosts && (
<button onClick={() => loadMorePosts()} disabled={loadingMorePosts}>
{loadingMorePosts ? 'Loading...' : 'Show More'}
</button>
)}
<style jsx>{`
section {
padding-bottom: 20px;
}
li {
display: block;
margin-bottom: 10px;
}
div {
align-items: center;
display: flex;
}
a {
font-size: 14px;
margin-right: 10px;
text-decoration: none;
padding-bottom: 0;
border: 0;
}
span {
font-size: 14px;
margin-right: 5px;
}
ul {
margin: 0;
padding: 0;
}
button:before {
align-self: center;
border-style: solid;
border-width: 6px 4px 0 4px;
border-color: #ffffff transparent transparent transparent;
content: '';
height: 0;
margin-right: 5px;
width: 0;
}
`}</style>
</section>
)
}
import { gql, useMutation } from '@apollo/client'
const UPDATE_POST_MUTATION = gql`
mutation votePost($id: String!) {
votePost(id: $id) {
id
votes
__typename
}
}
`
export default function PostUpvoter({ votes, id }) {
const [updatePost] = useMutation(UPDATE_POST_MUTATION)
const upvotePost = () => {
updatePost({
variables: {
id,
},
optimisticResponse: {
__typename: 'Mutation',
votePost: {
__typename: 'Post',
id,
votes: votes + 1,
},
},
})
}
return (
<button onClick={() => upvotePost()}>
{votes}
<style jsx>{`
button {
background-color: transparent;
border: 1px solid #e4e4e4;
color: #000;
}
button:active {
background-color: transparent;
}
button:before {
align-self: center;
border-color: transparent transparent #000000 transparent;
border-style: solid;
border-width: 0 4px 6px 4px;
content: '';
height: 0;
margin-right: 5px;
width: 0;
}
`}</style>
</button>
)
}
import { gql, useMutation } from '@apollo/client'
const CREATE_POST_MUTATION = gql`
mutation createPost($title: String!, $url: String!) {
createPost(title: $title, url: $url) {
id
title
votes
url
createdAt
}
}
`
export default function Submit() {
const [createPost, { loading }] = useMutation(CREATE_POST_MUTATION)
const handleSubmit = (event) => {
event.preventDefault()
const form = event.target
const formData = new window.FormData(form)
const title = formData.get('title')
const url = formData.get('url')
form.reset()
createPost({
variables: { title, url },
update: (cache, { data: { createPost } }) => {
cache.modify({
fields: {
allPosts(existingPosts = []) {
const newPostRef = cache.writeFragment({
data: createPost,
fragment: gql`
fragment NewPost on allPosts {
id
type
}
`,
})
return [newPostRef, ...existingPosts]
},
},
})
},
})
}
return (
<form onSubmit={handleSubmit}>
<h1>Submit</h1>
<input placeholder="title" name="title" type="text" required />
<input placeholder="url" name="url" type="url" required />
<button type="submit" disabled={loading}>
Submit
</button>
<style jsx>{`
form {
border-bottom: 1px solid #ececec;
padding-bottom: 20px;
margin-bottom: 20px;
}
h1 {
font-size: 20px;
}
input {
display: block;
margin-bottom: 10px;
}
`}</style>
</form>
)
}
module.exports = {
roots: ['<rootDir>'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'jsx'],
testPathIgnorePatterns: ['<rootDir>[/\\\\](node_modules|.next)[/\\\\]'],
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$'],
transform: {
'^.+\\.(ts|tsx)$': 'babel-jest',
'\\.graphql$': [
'graphql-let/jestTransformer',
{ subsequentTransformer: 'babel-jest' },
],
},
}
import { useMemo } from 'react'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
import { concatPagination } from '@apollo/client/utilities'
import merge from 'deepmerge'
import isEqual from 'lodash/isEqual'
import { useMemo } from "react";
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { concatPagination } from "@apollo/client/utilities";
import merge from "deepmerge";
export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__'
export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__";
let apolloClient
let apolloClient: any;
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === 'undefined',
ssrMode: typeof window === "undefined",
link: new HttpLink({
uri: 'https://nextjs-graphql-with-prisma-simple.vercel.app/api', // Server URL (must be absolute)
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
uri: "http://localhost:5000/graphql", // Server URL (must be absolute)
credentials: "same-origin", // Additional fetch() options like `credentials` or `headers`
}),
cache: new InMemoryCache({
typePolicies: {
......@@ -24,50 +23,42 @@ function createApolloClient() {
},
},
}),
})
});
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
const _apolloClient = apolloClient ?? createApolloClient();
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// gets hydrated here
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract()
const existingCache = _apolloClient.extract();
// Merge the existing cache into data passed from getStaticProps/getServerSideProps
const data = merge(initialState, existingCache, {
// combine arrays using object equality (like in sets)
arrayMerge: (destinationArray, sourceArray) => [
...sourceArray,
...destinationArray.filter((d) =>
sourceArray.every((s) => !isEqual(d, s))
),
],
})
const data = merge(initialState as any, existingCache);
// Restore the cache with the merged data
_apolloClient.cache.restore(data)
_apolloClient.cache.restore(data);
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient
if (typeof window === "undefined") return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient
return _apolloClient;
}
export function addApolloState(client, pageProps) {
export function addApolloState(client: any, pageProps: any) {
if (pageProps?.props) {
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract()
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
}
return pageProps
return pageProps;
}
export function useApollo(pageProps) {
const state = pageProps[APOLLO_STATE_PROP_NAME]
const store = useMemo(() => initializeApollo(state), [state])
return store
export function useApollo(pageProps: any) {
const state = pageProps[APOLLO_STATE_PROP_NAME];
const store = useMemo(() => initializeApollo(state), [state]);
return store;
}
......
/// <reference types="next" />
/// <reference types="next/types/global" />
declare module '*.graphqls' {
import { DocumentNode } from 'graphql'
export default typeof DocumentNode
}
declare module '*.yml'
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',
use: 'yaml-loader',
})
return config
},
}
{
"name": "@graphql-community/web",
"version": "1.0.0",
"version": "0.1.0",
"author": "",
"license": "MIT",
"scripts": {
"dev": "next",
"build": "next build",
"test": "jest",
"start": "next start"
},
"dependencies": {
"@apollo/client": "3.1.1",
"deepmerge": "^4.2.2",
"lodash": "4.17.20",
"graphql": "^15.3.0",
"@graphql-community/shared": "1.0.0",
"@apollo/client": "^3.1.3",
"@graphql-tools/load-files": "6.0.18",
"@graphql-tools/merge": "6.0.18",
"@graphql-tools/schema": "6.0.18",
"apollo-server-micro": "^2.16.1",
"graphql": "15.3.0",
"next": "latest",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-dom": "^16.7.0"
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"license": "MIT"
"devDependencies": {
"@graphql-codegen/cli": "^1.17.8",
"@graphql-codegen/plugin-helpers": "^1.17.8",
"@graphql-codegen/typescript": "^1.17.8",
"@graphql-codegen/typescript-operations": "^1.17.8",
"@graphql-codegen/typescript-react-apollo": "^2.0.6",
"@graphql-codegen/typescript-resolvers": "^1.17.8",
"@types/react": "^16.9.46",
"@types/react-dom": "^16.9.8",
"@types/react-test-renderer": "16.9.3",
"babel-jest": "26.3.0",
"graphql-let": "0.x",
"graphql-tag": "2.11.0",
"jest": "26.4.0",
"react-test-renderer": "16.13.1",
"typescript": "^3.9.7",
"yaml-loader": "0.6.0"
}
}
......
import { ApolloProvider } from '@apollo/client'
import { useApollo } from '../lib/apolloClient'
export default function App({ Component, pageProps }) {
const apolloClient = useApollo(pageProps)
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
)
}
import { AppProps } from "next/app";
import { ApolloProvider } from "@apollo/client";
import { useApollo } from "../lib/apollo";
export default function App({ Component, pageProps }: AppProps) {
const apolloClient = useApollo(pageProps);
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
);
}
import App from '../components/App'
import Header from '../components/Header'
const AboutPage = () => (
<App>
<Header />
<article>
<h1>The Idea Behind This Example</h1>
<p>
<a href="https://www.apollographql.com/client/">Apollo</a> is a GraphQL
client that allows you to easily query the exact data you need from a
GraphQL server. In addition to fetching and mutating data, Apollo
analyzes your queries and their results to construct a client-side cache
of your data, which is kept up to date as further queries and mutations
are run, fetching more results from the server.
</p>
<p>
In this simple example, we integrate Apollo seamlessly with{' '}
<a href="https://github.com/vercel/next.js">Next</a> by calling{' '}
<a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation">
getStaticProps
</a>{' '}
at our Page component. This approach lets us opt out of getInitialProps
and let us use all the niceties provided by{' '}
<a href="https://github.com/vercel/next.js">Next</a>.
</p>
<p>
On initial page load, while on the server and inside{' '}
<a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation">
getStaticProps
</a>
, we fetch the query used to get the list of posts. At the point in
which the query promise resolves, our Apollo Client store is completely
initialized. Then we serve the initial HTML with the fetched data and
hydrate Apollo in the browser.
</p>
<p>
This example relies on <a href="http://graph.cool">graph.cool</a> for
its GraphQL backend.
</p>
</article>
</App>
)
export default AboutPage
import Link from "next/link";
export default function About() {
return (
<div>
Welcome to the about page. Go to the{" "}
<Link href="/">
<a>Home</a>
</Link>{" "}
page.
</div>
);
}
import App from '../components/App'
import InfoBox from '../components/InfoBox'
import Header from '../components/Header'
import Submit from '../components/Submit'
import PostList from '../components/PostList'
const ClientOnlyPage = (props) => (
<App>
<Header />
<InfoBox>
ℹ️ This page shows how to use Apollo only in the client. If you{' '}
<a href="/client-only">reload</a> this page, you will see a loader since
Apollo didn't fetch any data on the server. This is useful when the page
doesn't have SEO requirements or blocking data fetching requirements.
</InfoBox>
<Submit />
<PostList />
</App>
)
export default ClientOnlyPage
import App from '../components/App'
import InfoBox from '../components/InfoBox'
import Header from '../components/Header'
import Submit from '../components/Submit'
import PostList, {
ALL_POSTS_QUERY,
allPostsQueryVars,
} from '../components/PostList'
import { initializeApollo, addApolloState } from '../lib/apolloClient'
const IndexPage = () => (
<App>
<Header />
<InfoBox>ℹ️ This page shows how to use SSG with Apollo.</InfoBox>
<Submit />
<PostList />
</App>
)
export async function getStaticProps() {
const apolloClient = initializeApollo()
await apolloClient.query({
query: ALL_POSTS_QUERY,
variables: allPostsQueryVars,
})
return addApolloState(apolloClient, {
props: {},
revalidate: 1,
})
}
export default IndexPage
import { GetPostInput, Post } from "@graphql-community/shared";
import { useQuery, gql } from "@apollo/client";
const GET_SOME_POST_QUERY = gql`
query GetSomePosts($getSomePostInput: GetPostInput!) {
getSomePosts(input: $getSomePostInput) {
author
category
}
}
`;
const Index = () => {
const { data, error } = useQuery<
{ getSomePosts: Post[] },
{ getSomePostInput: GetPostInput }
>(GET_SOME_POST_QUERY, {
variables: {
getSomePostInput: {
id: 1,
},
},
});
if (error) console.log(JSON.stringify(error, null, 2));
return (
<>
<div>index </div>
<div>{data?.getSomePosts[0].author}</div>
<div>{data?.getSomePosts[0].category}</div>
</>
);
};
export default Index;
import App from '../components/App'
import InfoBox from '../components/InfoBox'
import Header from '../components/Header'
import Submit from '../components/Submit'
import PostList, {
ALL_POSTS_QUERY,
allPostsQueryVars,
} from '../components/PostList'
import { initializeApollo, addApolloState } from '../lib/apolloClient'
const SSRPage = () => (
<App>
<Header />
<InfoBox>ℹ️ This page shows how to use SSR with Apollo.</InfoBox>
<Submit />
<PostList />
</App>
)
export async function getServerSideProps() {
const apolloClient = initializeApollo()
await apolloClient.query({
query: ALL_POSTS_QUERY,
variables: allPostsQueryVars,
})
return addApolloState(apolloClient, {
props: {},
})
}
export default SSRPage
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Index renders the html we want 1`] = `
<div>
You're signed in as
Baa
and you're
Healthy
. Go to the
<a
href="/about"
onClick={[Function]}
onMouseEnter={[Function]}
>
about
</a>
page.
<div>
<input
onChange={[Function]}
placeholder="your new name..."
type="text"
/>
<input
onClick={[Function]}
type="button"
value="change"
/>
</div>
</div>
`;
import { InMemoryCache, gql } from '@apollo/client'
import React from 'react'
import Index from '../pages'
import renderer from 'react-test-renderer'
import { MockedProvider } from '@apollo/client/testing'
const cache = new InMemoryCache()
cache.writeQuery({
query: gql`
query Viewer {
viewer {
id
name
status
}
}
`,
data: {
viewer: {
__typename: 'User',
id: 'Baa',
name: 'Baa',
status: 'Healthy',
},
},
})
describe('Index', () => {
it('renders the html we want', async () => {
const component = renderer.create(
<MockedProvider cache={cache}>
<Index />
</MockedProvider>
)
expect(component.toJSON()).toMatchSnapshot()
})
})
{
"compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": [
"dom",
"es2017"
],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"target": "esnext",
"strict": false
},
"exclude": [
"node_modules"
],
"include": [
"**/*.ts",
"**/*.tsx"
]
}
This diff is collapsed. Click to expand it.