diff --git a/examples/with-grafbase/.env.local.example b/examples/with-grafbase/.env.local.example
new file mode 100644
index 000000000000..b1de6720459f
--- /dev/null
+++ b/examples/with-grafbase/.env.local.example
@@ -0,0 +1,2 @@
+GRAFBASE_API_URL=http://localhost:4000/graphql
+GRAFBASE_API_KEY=
diff --git a/examples/with-grafbase/.gitignore b/examples/with-grafbase/.gitignore
new file mode 100644
index 000000000000..315d86ad9b20
--- /dev/null
+++ b/examples/with-grafbase/.gitignore
@@ -0,0 +1,38 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+.grafbase
\ No newline at end of file
diff --git a/examples/with-grafbase/.vscode/settings.json b/examples/with-grafbase/.vscode/settings.json
new file mode 100644
index 000000000000..fae8e3d8a976
--- /dev/null
+++ b/examples/with-grafbase/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "typescript.tsdk": "node_modules/typescript/lib",
+ "typescript.enablePromptUseWorkspaceTsdk": true
+}
diff --git a/examples/with-grafbase/README.md b/examples/with-grafbase/README.md
new file mode 100644
index 000000000000..e5e1ae94b314
--- /dev/null
+++ b/examples/with-grafbase/README.md
@@ -0,0 +1,68 @@
+# Next.js with Grafbase
+
+This example shows to use [Grafbase](https://grafbase.com) with Next.js. This example features fetching from a local GraphQL backend powered by the Grafbase CLI, and GraphQL Code Generator for making type-safe queries.
+
+## Demo
+
+You can see a demo of this online at [https://grafbase-with-nextjs-rsc.grafbase-vercel.dev](https://grafbase-with-nextjs-rsc.grafbase-vercel.dev).
+
+## Deploy
+
+First deploy this to Grafbase to get your backend API URL and Key:
+
+[![Deploy to Grafbase](https://grafbase.com/button)](https://grafbase.com/new/configure?template=NextExample&source=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-grafbase)
+
+Then deploy this example using [Vercel](https://vercel.com):
+
+[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-grafbase&env=GRAFBASE_API_URL,GRAFBASE_API_KEY)
+
+## 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), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
+
+```bash
+npx create-next-app --example with-grafbase with-grafbase-app
+```
+
+```bash
+yarn create next-app --example with-grafbase with-grafbase-app
+```
+
+```bash
+pnpm create next-app --example with-grafbase with-grafbase-app
+```
+
+To run the example locally you need to:
+
+1. Copy the `.env.local.example` to `.env.local` and provide your API URL and API Key: `cp .env.local.example .env.local` — the defaults will be fine for development mode.
+
+2. Run the [Grafbase CLI](https://grafbase.com/cli) using `npx grafbase@latest dev`
+
+3. Populate the backend with some `Post` entries using a GraphQL mutation:
+
+```graphql
+mutation {
+ postCreate(
+ input: {
+ title: "I love Next.js!"
+ slug: "i-love-nextjs"
+ comments: [{ create: { message: "me too!" } }]
+ }
+ ) {
+ post {
+ id
+ slug
+ }
+ }
+}
+```
+
+4. Run the app locally and go to [http://localhost:3000](http://localhost:3000) to navigate to each post page! This data is fetched from the local backend.
+
+5. Optionally run `npm run codegen` to watch for any changes to queries inside of the app and automatically generate types.
+
+## Learn more
+
+- [Grafbase Quickstart](https://grafbase.com/docs/quickstart/get-started) — get started with Grafbase, quickly!
+- [Create an account](https://grafbase.com/sign-up) — deploy to the edge with Grafbase!
+- [Next.js Documentation](https://nextjs.org/docs) — learn more about Next.js
diff --git a/examples/with-grafbase/app/globals.css b/examples/with-grafbase/app/globals.css
new file mode 100644
index 000000000000..b5c61c956711
--- /dev/null
+++ b/examples/with-grafbase/app/globals.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/examples/with-grafbase/app/head.tsx b/examples/with-grafbase/app/head.tsx
new file mode 100644
index 000000000000..5b847ecacdb2
--- /dev/null
+++ b/examples/with-grafbase/app/head.tsx
@@ -0,0 +1,7 @@
+const Head = () => (
+ <>
+
Grafbase + Next.js
+ >
+)
+
+export default Head
diff --git a/examples/with-grafbase/app/layout.tsx b/examples/with-grafbase/app/layout.tsx
new file mode 100644
index 000000000000..45c191591861
--- /dev/null
+++ b/examples/with-grafbase/app/layout.tsx
@@ -0,0 +1,80 @@
+import './globals.css'
+
+import Link from 'next/link'
+
+import { graphql } from '../gql'
+import { grafbase } from '../lib/grafbase'
+
+const GetAllPostsDocument = graphql(/* GraphQL */ `
+ query GetAllPosts($first: Int!) {
+ postCollection(first: $first) {
+ edges {
+ node {
+ id
+ title
+ slug
+ }
+ }
+ }
+ }
+`)
+
+const RootLayout = async ({ children }: { children: React.ReactNode }) => {
+ const { postCollection } = await grafbase.request(GetAllPostsDocument, {
+ first: 50,
+ })
+
+ return (
+
+
+ Grafbase + Next.js 13
+
+
+
+
+
+
+
+ Home
+
+
+
+ Posts
+
+ {postCollection?.edges?.map((edge) =>
+ edge?.node ? (
+
+
+ {edge.node.title}
+
+
+ ) : null
+ )}
+
+
+ Show 404 page
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default RootLayout
diff --git a/examples/with-grafbase/app/page.tsx b/examples/with-grafbase/app/page.tsx
new file mode 100644
index 000000000000..fb61c3b6b626
--- /dev/null
+++ b/examples/with-grafbase/app/page.tsx
@@ -0,0 +1,13 @@
+const Page = async () => {
+ return (
+ <>
+ Next.js 13 + Grafbase
+
+ Once you've added some posts using the GraphQL Playground, you can
+ explore each post by clicking the link in the nav.
+
+ >
+ )
+}
+
+export default Page
diff --git a/examples/with-grafbase/app/posts/[slug]/page.tsx b/examples/with-grafbase/app/posts/[slug]/page.tsx
new file mode 100644
index 000000000000..2443b0f1ef55
--- /dev/null
+++ b/examples/with-grafbase/app/posts/[slug]/page.tsx
@@ -0,0 +1,34 @@
+import { graphql } from '../../../gql'
+import { grafbase } from '../../../lib/grafbase'
+
+export const revalidate = 3600
+
+const GetPostBySlugDocument = graphql(/* GraphQL */ `
+ query GetPostBySlug($slug: String!) {
+ post(by: { slug: $slug }) {
+ id
+ title
+ slug
+ }
+ }
+`)
+
+const Page = async ({ params }: { params: { slug: string } }) => {
+ const { post } = await grafbase.request(GetPostBySlugDocument, {
+ slug: params.slug,
+ })
+
+ if (!post) {
+ // optionally import notFound from next/navigation
+ return 404: Not Found
+ }
+
+ return (
+ <>
+ {post.title}
+ {JSON.stringify(post, null, 2)}
+ >
+ )
+}
+
+export default Page
diff --git a/examples/with-grafbase/codegen.ts b/examples/with-grafbase/codegen.ts
new file mode 100644
index 000000000000..28164af1fc11
--- /dev/null
+++ b/examples/with-grafbase/codegen.ts
@@ -0,0 +1,26 @@
+import { CodegenConfig } from '@graphql-codegen/cli'
+
+const url = process.env.GRAFBASE_API_URL as string
+const xApiKey = process.env.GRAFBASE_API_KEY as string
+
+const config: CodegenConfig = {
+ schema: [
+ {
+ [url]: {
+ headers: {
+ 'x-api-key': xApiKey,
+ },
+ },
+ },
+ ],
+ documents: ['app/**/*.tsx', 'app/**/*.ts'],
+ ignoreNoDocuments: true,
+ generates: {
+ './gql/': {
+ preset: 'client',
+ plugins: [],
+ },
+ },
+}
+
+export default config
diff --git a/examples/with-grafbase/gql/fragment-masking.ts b/examples/with-grafbase/gql/fragment-masking.ts
new file mode 100644
index 000000000000..4de725491dd3
--- /dev/null
+++ b/examples/with-grafbase/gql/fragment-masking.ts
@@ -0,0 +1,44 @@
+import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
+
+export type FragmentType> =
+ TDocumentType extends DocumentNode
+ ? TType extends { ' $fragmentName'?: infer TKey }
+ ? TKey extends string
+ ? { ' $fragmentRefs'?: { [key in TKey]: TType } }
+ : never
+ : never
+ : never
+
+// return non-nullable if `fragmentType` is non-nullable
+export function useFragment(
+ _documentNode: DocumentNode,
+ fragmentType: FragmentType>
+): TType
+// return nullable if `fragmentType` is nullable
+export function useFragment(
+ _documentNode: DocumentNode,
+ fragmentType: FragmentType> | null | undefined
+): TType | null | undefined
+// return array of non-nullable if `fragmentType` is array of non-nullable
+export function useFragment(
+ _documentNode: DocumentNode,
+ fragmentType: ReadonlyArray>>
+): ReadonlyArray
+// return array of nullable if `fragmentType` is array of nullable
+export function useFragment(
+ _documentNode: DocumentNode,
+ fragmentType:
+ | ReadonlyArray>>
+ | null
+ | undefined
+): ReadonlyArray | null | undefined
+export function useFragment(
+ _documentNode: DocumentNode,
+ fragmentType:
+ | FragmentType>
+ | ReadonlyArray>>
+ | null
+ | undefined
+): TType | ReadonlyArray | null | undefined {
+ return fragmentType as any
+}
diff --git a/examples/with-grafbase/gql/gql.ts b/examples/with-grafbase/gql/gql.ts
new file mode 100644
index 000000000000..6bf7a46bc9f8
--- /dev/null
+++ b/examples/with-grafbase/gql/gql.ts
@@ -0,0 +1,25 @@
+/* eslint-disable */
+import * as types from './graphql'
+import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
+
+const documents = {
+ '\n query GetAllPosts($first: Int!) {\n postCollection(first: $first) {\n edges {\n node {\n id\n title\n slug\n }\n }\n }\n }\n':
+ types.GetAllPostsDocument,
+ '\n query GetPostBySlug($slug: String!) {\n post(by: { slug: $slug }) {\n id\n title\n slug\n }\n }\n':
+ types.GetPostBySlugDocument,
+}
+
+export function graphql(
+ source: '\n query GetAllPosts($first: Int!) {\n postCollection(first: $first) {\n edges {\n node {\n id\n title\n slug\n }\n }\n }\n }\n'
+): typeof documents['\n query GetAllPosts($first: Int!) {\n postCollection(first: $first) {\n edges {\n node {\n id\n title\n slug\n }\n }\n }\n }\n']
+export function graphql(
+ source: '\n query GetPostBySlug($slug: String!) {\n post(by: { slug: $slug }) {\n id\n title\n slug\n }\n }\n'
+): typeof documents['\n query GetPostBySlug($slug: String!) {\n post(by: { slug: $slug }) {\n id\n title\n slug\n }\n }\n']
+
+export function graphql(source: string): unknown
+export function graphql(source: string) {
+ return (documents as any)[source] ?? {}
+}
+
+export type DocumentType> =
+ TDocumentNode extends DocumentNode ? TType : never
diff --git a/examples/with-grafbase/gql/graphql.ts b/examples/with-grafbase/gql/graphql.ts
new file mode 100644
index 000000000000..97ba10d05a02
--- /dev/null
+++ b/examples/with-grafbase/gql/graphql.ts
@@ -0,0 +1,444 @@
+/* eslint-disable */
+import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
+export type Maybe = T | null
+export type InputMaybe = Maybe
+export type Exact = {
+ [K in keyof T]: T[K]
+}
+export type MakeOptional = Omit & {
+ [SubKey in K]?: Maybe
+}
+export type MakeMaybe = Omit & {
+ [SubKey in K]: Maybe
+}
+/** All built-in and custom scalars, mapped to their actual values */
+export type Scalars = {
+ ID: string
+ String: string
+ Boolean: boolean
+ Int: number
+ Float: number
+ /**
+ * A date-time string at UTC, such as 2007-12-03T10:15:30Z, is compliant with the date-time format outlined in section 5.6 of the RFC 3339
+ * profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
+ *
+ * This scalar is a description of an exact instant on the timeline such as the instant that a user account was created.
+ *
+ * # Input Coercion
+ *
+ * When expected as an input type, only RFC 3339 compliant date-time strings are accepted. All other input values raise a query error indicating an incorrect type.
+ *
+ * # Result Coercion
+ *
+ * Where an RFC 3339 compliant date-time string has a time-zone other than UTC, it is shifted to UTC.
+ * For example, the date-time string 2016-01-01T14:10:20+01:00 is shifted to 2016-01-01T13:10:20Z.
+ */
+ DateTime: any
+}
+
+export type Comment = {
+ __typename?: 'Comment'
+ /** when the model was created */
+ createdAt: Scalars['DateTime']
+ id: Scalars['ID']
+ message: Scalars['String']
+ post?: Maybe
+ /** when the model was updated */
+ updatedAt: Scalars['DateTime']
+}
+
+export type CommentByInput = {
+ id?: InputMaybe
+}
+
+/** Input to create a new CommentCommentRelatePostPost */
+export type CommentCommentRelatePostPostCreateInput = {
+ slug: Scalars['String']
+ title: Scalars['String']
+}
+
+/** Input to create a new CommentCommentRelatePostPost relation */
+export type CommentCommentRelatePostPostCreateRelationInput = {
+ create?: InputMaybe
+ link?: InputMaybe
+}
+
+/** Input to update a CommentCommentRelatePostPost relation */
+export type CommentCommentRelatePostPostUpdateRelationInput = {
+ create?: InputMaybe
+ link?: InputMaybe
+ unlink?: InputMaybe
+}
+
+export type CommentConnection = {
+ __typename?: 'CommentConnection'
+ edges?: Maybe>>
+ /** Information to aid in pagination */
+ pageInfo: PageInfo
+}
+
+/** Input to create a new Comment */
+export type CommentCreateInput = {
+ message: Scalars['String']
+ post?: InputMaybe
+}
+
+export type CommentCreatePayload = {
+ __typename?: 'CommentCreatePayload'
+ comment?: Maybe
+}
+
+export type CommentDeletePayload = {
+ __typename?: 'CommentDeletePayload'
+ deletedId: Scalars['ID']
+}
+
+export type CommentEdge = {
+ __typename?: 'CommentEdge'
+ cursor: Scalars['String']
+ node: Comment
+}
+
+/** Input to create a new Comment */
+export type CommentUpdateInput = {
+ message?: InputMaybe
+ post?: InputMaybe
+}
+
+export type CommentUpdatePayload = {
+ __typename?: 'CommentUpdatePayload'
+ comment?: Maybe
+}
+
+export type Mutation = {
+ __typename?: 'Mutation'
+ /** Create a Comment */
+ commentCreate?: Maybe
+ /** Delete a Comment by ID or unique field */
+ commentDelete?: Maybe
+ /** Update a Comment */
+ commentUpdate?: Maybe
+ /** Create a Post */
+ postCreate?: Maybe
+ /** Delete a Post by ID or unique field */
+ postDelete?: Maybe
+ /** Update a Post */
+ postUpdate?: Maybe
+}
+
+export type MutationCommentCreateArgs = {
+ input: CommentCreateInput
+}
+
+export type MutationCommentDeleteArgs = {
+ by: CommentByInput
+}
+
+export type MutationCommentUpdateArgs = {
+ by: CommentByInput
+ input: CommentUpdateInput
+}
+
+export type MutationPostCreateArgs = {
+ input: PostCreateInput
+}
+
+export type MutationPostDeleteArgs = {
+ by: PostByInput
+}
+
+export type MutationPostUpdateArgs = {
+ by: PostByInput
+ input: PostUpdateInput
+}
+
+export type PageInfo = {
+ __typename?: 'PageInfo'
+ endCursor?: Maybe
+ hasNextPage: Scalars['Boolean']
+ hasPreviousPage: Scalars['Boolean']
+ startCursor?: Maybe
+}
+
+export type Post = {
+ __typename?: 'Post'
+ comments?: Maybe
+ /** when the model was created */
+ createdAt: Scalars['DateTime']
+ id: Scalars['ID']
+ slug: Scalars['String']
+ title: Scalars['String']
+ /** when the model was updated */
+ updatedAt: Scalars['DateTime']
+}
+
+export type PostCommentsArgs = {
+ after?: InputMaybe
+ before?: InputMaybe
+ first?: InputMaybe
+ last?: InputMaybe
+}
+
+export type PostByInput = {
+ id?: InputMaybe
+ slug?: InputMaybe
+}
+
+/** Input to create a new PostCommentRelatePostComment */
+export type PostCommentRelatePostCommentCreateInput = {
+ message: Scalars['String']
+}
+
+/** Input to create a new PostCommentRelatePostComment relation */
+export type PostCommentRelatePostCommentCreateRelationInput = {
+ create?: InputMaybe
+ link?: InputMaybe
+}
+
+/** Input to update a PostCommentRelatePostComment relation */
+export type PostCommentRelatePostCommentUpdateRelationInput = {
+ create?: InputMaybe
+ link?: InputMaybe
+ unlink?: InputMaybe
+}
+
+export type PostConnection = {
+ __typename?: 'PostConnection'
+ edges?: Maybe>>
+ /** Information to aid in pagination */
+ pageInfo: PageInfo
+}
+
+/** Input to create a new Post */
+export type PostCreateInput = {
+ comments?: InputMaybe<
+ Array>
+ >
+ slug: Scalars['String']
+ title: Scalars['String']
+}
+
+export type PostCreatePayload = {
+ __typename?: 'PostCreatePayload'
+ post?: Maybe
+}
+
+export type PostDeletePayload = {
+ __typename?: 'PostDeletePayload'
+ deletedId: Scalars['ID']
+}
+
+export type PostEdge = {
+ __typename?: 'PostEdge'
+ cursor: Scalars['String']
+ node: Post
+}
+
+/** Input to create a new Post */
+export type PostUpdateInput = {
+ comments?: InputMaybe<
+ Array>
+ >
+ slug?: InputMaybe
+ title?: InputMaybe
+}
+
+export type PostUpdatePayload = {
+ __typename?: 'PostUpdatePayload'
+ post?: Maybe
+}
+
+export type Query = {
+ __typename?: 'Query'
+ /** Query a single Comment by an ID or a unique field */
+ comment?: Maybe
+ /** Paginated query to fetch the whole list of `Comment`. */
+ commentCollection?: Maybe
+ /** Query a single Post by an ID or a unique field */
+ post?: Maybe
+ /** Paginated query to fetch the whole list of `Post`. */
+ postCollection?: Maybe
+}
+
+export type QueryCommentArgs = {
+ by: CommentByInput
+}
+
+export type QueryCommentCollectionArgs = {
+ after?: InputMaybe
+ before?: InputMaybe
+ first?: InputMaybe
+ last?: InputMaybe
+}
+
+export type QueryPostArgs = {
+ by: PostByInput
+}
+
+export type QueryPostCollectionArgs = {
+ after?: InputMaybe
+ before?: InputMaybe
+ first?: InputMaybe
+ last?: InputMaybe
+}
+
+export type GetAllPostsQueryVariables = Exact<{
+ first: Scalars['Int']
+}>
+
+export type GetAllPostsQuery = {
+ __typename?: 'Query'
+ postCollection?: {
+ __typename?: 'PostConnection'
+ edges?: Array<{
+ __typename?: 'PostEdge'
+ node: { __typename?: 'Post'; id: string; title: string; slug: string }
+ } | null> | null
+ } | null
+}
+
+export type GetPostBySlugQueryVariables = Exact<{
+ slug: Scalars['String']
+}>
+
+export type GetPostBySlugQuery = {
+ __typename?: 'Query'
+ post?: { __typename?: 'Post'; id: string; title: string; slug: string } | null
+}
+
+export const GetAllPostsDocument = {
+ kind: 'Document',
+ definitions: [
+ {
+ kind: 'OperationDefinition',
+ operation: 'query',
+ name: { kind: 'Name', value: 'GetAllPosts' },
+ variableDefinitions: [
+ {
+ kind: 'VariableDefinition',
+ variable: {
+ kind: 'Variable',
+ name: { kind: 'Name', value: 'first' },
+ },
+ type: {
+ kind: 'NonNullType',
+ type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } },
+ },
+ },
+ ],
+ selectionSet: {
+ kind: 'SelectionSet',
+ selections: [
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'postCollection' },
+ arguments: [
+ {
+ kind: 'Argument',
+ name: { kind: 'Name', value: 'first' },
+ value: {
+ kind: 'Variable',
+ name: { kind: 'Name', value: 'first' },
+ },
+ },
+ ],
+ selectionSet: {
+ kind: 'SelectionSet',
+ selections: [
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'edges' },
+ selectionSet: {
+ kind: 'SelectionSet',
+ selections: [
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'node' },
+ selectionSet: {
+ kind: 'SelectionSet',
+ selections: [
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'id' },
+ },
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'title' },
+ },
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'slug' },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+} as unknown as DocumentNode
+export const GetPostBySlugDocument = {
+ kind: 'Document',
+ definitions: [
+ {
+ kind: 'OperationDefinition',
+ operation: 'query',
+ name: { kind: 'Name', value: 'GetPostBySlug' },
+ variableDefinitions: [
+ {
+ kind: 'VariableDefinition',
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'slug' } },
+ type: {
+ kind: 'NonNullType',
+ type: {
+ kind: 'NamedType',
+ name: { kind: 'Name', value: 'String' },
+ },
+ },
+ },
+ ],
+ selectionSet: {
+ kind: 'SelectionSet',
+ selections: [
+ {
+ kind: 'Field',
+ name: { kind: 'Name', value: 'post' },
+ arguments: [
+ {
+ kind: 'Argument',
+ name: { kind: 'Name', value: 'by' },
+ value: {
+ kind: 'ObjectValue',
+ fields: [
+ {
+ kind: 'ObjectField',
+ name: { kind: 'Name', value: 'slug' },
+ value: {
+ kind: 'Variable',
+ name: { kind: 'Name', value: 'slug' },
+ },
+ },
+ ],
+ },
+ },
+ ],
+ selectionSet: {
+ kind: 'SelectionSet',
+ selections: [
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
+ { kind: 'Field', name: { kind: 'Name', value: 'title' } },
+ { kind: 'Field', name: { kind: 'Name', value: 'slug' } },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+} as unknown as DocumentNode
diff --git a/examples/with-grafbase/gql/index.ts b/examples/with-grafbase/gql/index.ts
new file mode 100644
index 000000000000..99775ae23cb0
--- /dev/null
+++ b/examples/with-grafbase/gql/index.ts
@@ -0,0 +1,2 @@
+export * from './gql'
+export * from './fragment-masking'
diff --git a/examples/with-grafbase/grafbase/schema.graphql b/examples/with-grafbase/grafbase/schema.graphql
new file mode 100644
index 000000000000..4acbcc7e29fe
--- /dev/null
+++ b/examples/with-grafbase/grafbase/schema.graphql
@@ -0,0 +1,12 @@
+type Post @model {
+ id: ID!
+ title: String!
+ slug: String! @unique
+ comments: [Comment]
+}
+
+type Comment @model {
+ id: ID!
+ message: String!
+ post: Post
+}
diff --git a/examples/with-grafbase/lib/grafbase.ts b/examples/with-grafbase/lib/grafbase.ts
new file mode 100644
index 000000000000..d2cf5dc2f1ab
--- /dev/null
+++ b/examples/with-grafbase/lib/grafbase.ts
@@ -0,0 +1,11 @@
+import { GraphQLClient } from 'graphql-request'
+export { gql } from 'graphql-request'
+
+export const grafbase = new GraphQLClient(
+ process.env.GRAFBASE_API_URL as string,
+ {
+ headers: {
+ 'x-api-key': process.env.GRAFBASE_API_KEY as string,
+ },
+ }
+)
diff --git a/examples/with-grafbase/next.config.js b/examples/with-grafbase/next.config.js
new file mode 100644
index 000000000000..c73b884cee31
--- /dev/null
+++ b/examples/with-grafbase/next.config.js
@@ -0,0 +1,10 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+ swcMinify: true,
+ experimental: {
+ appDir: true,
+ },
+}
+
+module.exports = nextConfig
diff --git a/examples/with-grafbase/package.json b/examples/with-grafbase/package.json
new file mode 100644
index 000000000000..4ada5b1a9baf
--- /dev/null
+++ b/examples/with-grafbase/package.json
@@ -0,0 +1,29 @@
+{
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "backend": "npx grafbase@latest dev & yarn codegen",
+ "build": "next build",
+ "start": "next start",
+ "codegen": "graphql-codegen --watch -r dotenv/config"
+ },
+ "dependencies": {
+ "@types/node": "18.11.9",
+ "@types/react": "18.0.25",
+ "@types/react-dom": "18.0.8",
+ "graphql": "16.6.0",
+ "graphql-request": "5.0.0",
+ "next": "latest",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "typescript": "4.8.4"
+ },
+ "devDependencies": {
+ "@graphql-codegen/cli": "2.13.12",
+ "@graphql-codegen/client-preset": "1.1.3",
+ "@tailwindcss/typography": "0.5.8",
+ "autoprefixer": "10.4.13",
+ "postcss": "8.4.19",
+ "tailwindcss": "3.2.4"
+ }
+}
diff --git a/examples/with-grafbase/postcss.config.js b/examples/with-grafbase/postcss.config.js
new file mode 100644
index 000000000000..33ad091d26d8
--- /dev/null
+++ b/examples/with-grafbase/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/examples/with-grafbase/tailwind.config.js b/examples/with-grafbase/tailwind.config.js
new file mode 100644
index 000000000000..d4b5c6122d0a
--- /dev/null
+++ b/examples/with-grafbase/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ['./app/**/*.{js,ts,jsx,tsx}'],
+ theme: {
+ extend: {},
+ },
+ plugins: [require('@tailwindcss/typography')],
+}
diff --git a/examples/with-grafbase/tsconfig.json b/examples/with-grafbase/tsconfig.json
new file mode 100644
index 000000000000..6ef5cd577f6e
--- /dev/null
+++ b/examples/with-grafbase/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}