+}
+export default withWunderGraph(Home)
+```
+
+## Global Configuration
+
+You can configure the hooks globally by using the [SWRConfig](https://swr.vercel.app/docs/global-configuration) context.
+
+In case the context configuration isn't working, it's likely due to multiple versions of SWR being installed or due to how PNPM or Yarn PnP link packages.
+To resolve this you can import SWR directly from `@wundergraph/nextjs` to make sure the same instance is used.
+
+```ts
+import { SWRConfig, useSWRConfig } from '@wundergraph/nextjs'
+```
diff --git a/docs-website/src/pages/docs/clients-reference/react-query.md b/docs-website/src/pages/docs/clients-reference/react-query.md
new file mode 100644
index 000000000..e4cda110b
--- /dev/null
+++ b/docs-website/src/pages/docs/clients-reference/react-query.md
@@ -0,0 +1,161 @@
+---
+title: React Query Client
+pageTitle: WunderGraph React Query Client
+description: React Query Client reference
+---
+
+This package provides a type-safe integration of [React Query](https://tanstack.com/query/v4/docs/overview) with WunderGraph.
+React Query is a data fetching library for React. With just one hook, you can significantly simplify the data fetching logic in your project. And it also covered in all aspects of speed, correctness, and stability to help you build better experiences.
+
+## Installation
+
+```shell
+npm install @wundergraph/react-query @tanstack/react-query
+```
+
+## Configuration
+
+Before you can use the hooks, you need to modify your code generation to include the base typescript client.
+
+```typescript
+// wundergraph.config.ts
+configureWunderGraphApplication({
+ // ... omitted for brevity
+ codeGenerators: [
+ {
+ templates: [templates.typescript.client],
+ // the location where you want to generate the client
+ path: '../src/components/generated',
+ },
+ ],
+})
+```
+
+Now you can configure the hooks. Create a new file, for example `lib/wundergraph.ts` and add the following code:
+
+```ts
+import { createHooks } from '@wundergraph/react-query'
+import { createClient, Operations } from './components/generated/client'
+
+const client = createClient() // Typesafe WunderGraph client
+
+export const {
+ useQuery,
+ useMutation,
+ useSubscription,
+ useUser,
+ useFileUpload,
+ useAuth,
+ queryKey,
+} = createHooks(client)
+```
+
+In your `App.tsx` add QueryClientProvider:
+
+```tsx
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+const queryClient = new QueryClient();
+
+export default App() {
+ return (
+
+
...
+
+ );
+}
+```
+
+## Usage
+
+Now you can use the hooks in your components:
+
+### useQuery
+
+```ts
+const { data, error, isLoading } = useQuery({
+ operationName: 'Weather',
+ input: { forCity: city },
+})
+```
+
+### useQuery (Live query)
+
+```ts
+const { data, error, isLoading, isSubscribed } = useQuery({
+ operationName: 'Weather',
+ input: { forCity: city },
+ liveQuery: true,
+})
+```
+
+### useSubscription
+
+```ts
+const { data, error, isLoading, isSubscribed } = useSubscription({
+ operationName: 'Weather',
+ input: {
+ forCity: 'Berlin',
+ },
+})
+```
+
+### useMutation
+
+```ts
+const { data, mutate, mutateAsync } = useMutation({
+ operationName: 'SetName',
+})
+
+mutate({ name: 'WunderGraph' })
+
+await mutateAsync({ name: 'WunderGraph' })
+```
+
+### useFileUpload
+
+```ts
+const { upload, data: fileKeys, error } = useFileUpload()
+
+upload({
+ provider: 'minio',
+ files: new FileList(),
+})
+```
+
+### useAuth
+
+```ts
+const { login, logout } = useAuth()
+
+login('github')
+
+logout({ logoutOpenidConnectProvider: true })
+```
+
+### useUser
+
+```ts
+const { data: user, error } = useUser()
+```
+
+### queryKey
+
+You can use the `queryKey` helper function to create a unique key for the query in a typesafe way. This is useful if you want to invalidate the query after mutating.
+
+```ts
+const queryClient = useQueryClient()
+
+const { mutate, mutateAsync } = useMutation({
+ operationName: 'SetName',
+ onSuccess() {
+ queryClient.invalidateQueries(queryKey({ operationName: 'Profile' }))
+ },
+})
+
+mutate({ name: 'WunderGraph' })
+```
+
+## Options
+
+You can use all available options from [React Query](https://tanstack.com/query/v4/docs/reference/useQuery) with the hooks.
diff --git a/docs-website/src/pages/docs/clients-reference/swr.md b/docs-website/src/pages/docs/clients-reference/swr.md
new file mode 100644
index 000000000..3611620d7
--- /dev/null
+++ b/docs-website/src/pages/docs/clients-reference/swr.md
@@ -0,0 +1,169 @@
+---
+title: SWR Client
+pageTitle: WunderGraph SWR Client
+description: SWR Client reference
+---
+
+The SWR Client is our default client for React projects. It's a lightweight wrapper around the [SWR](https://swr.vercel.app/) library from Vercel.
+
+## Installation
+
+> Note: The client depends on SWR version 2.0.0-rc.0.
+
+```bash
+npm i @wundergraph/swr swr@2.0.0-rc.0
+```
+
+## Configuration
+
+Let's start by configuring WunderGraph. We're using `templates.typescript.client` to generate our base client that will be used to create typesafe hooks.
+
+```typescript
+// wundergraph.config.ts
+configureWunderGraphApplication({
+ // ... your configuration
+ codeGenerators: [
+ {
+ templates: [templates.typescript.client],
+ path: '../generated',
+ },
+ ],
+})
+```
+
+## Create the hooks
+
+WunderGraph comes with a powerful framework for generating code.
+Here we are creating fully typed SWR hooks based on the operations of our WunderGraph application.
+
+```ts
+// lib/wundergraph.ts
+import { createClient, Operations } from '../generated/client'
+
+import { createHooks } from '@wundergraph/swr'
+
+export const client = createClient()
+
+export const { useQuery, useMutation, useSubscription, useUser, useAuth } =
+ createHooks(client)
+```
+
+## Hooks
+
+### useQuery
+
+This hook accepts most [useSWR Options](https://swr.vercel.app/docs/options) except for key and fetcher.
+
+```typescript
+const { data, error, isValidating, isLoading, mutate } = useQuery({
+ operationName: 'Weather',
+ input: {
+ forCity: 'Berlin',
+ },
+ enabled: true,
+})
+```
+
+Calling `mutate` will invalidate and refetch the query.
+
+```typescript
+const { data, mutate } = useQuery({
+ operationName: 'Weather',
+ input: {
+ forCity: 'Berlin',
+ },
+})
+
+mutate()
+```
+
+### useQuery (Live query)
+
+You can turn any query into a live query by adding the `liveQuery` option.
+
+```typescript
+const { data, error, isLoading, isSubscribed, mutate } = useQuery({
+ operationName: 'Weather',
+ input: {
+ forCity: 'Berlin',
+ },
+ liveQuery: true,
+})
+```
+
+### useMutation
+
+This hook accepts most [useSWRMutation Options](https://swr.vercel.app/docs/options) except for key and fetcher.
+
+```typescript
+const { data, error, trigger } = useMutation({
+ operationName: 'SetName',
+})
+
+await trigger({
+ name: 'test',
+})
+
+trigger(
+ {
+ name: 'test',
+ },
+ {
+ throwOnError: false,
+ }
+)
+```
+
+### useSubscription
+
+```typescript
+const { data, error, isLoading, isSubscribed } = useSubscription({
+ operationName: 'Weather',
+ input: {
+ forCity: 'Berlin',
+ },
+ enabled: true,
+ onSuccess(data, key, config) {
+ // called when the subscription is established.
+ },
+ onError(data, key, config) {
+ // called when the subscription failed to establish.
+ },
+})
+```
+
+### useAuth
+
+```typescript
+const { login, logout } = useAuth()
+
+login('github')
+
+logout({ logoutOpenidConnectProvider: true })
+```
+
+### useUser
+
+This hook accepts most [useSWR Options](https://swr.vercel.app/docs/options) except for key and fetcher.
+
+```typescript
+const { data, error, isLoading } = useUser()
+```
+
+## File upload
+
+This hook accepts most [useSWRMutation Options](https://swr.vercel.app/docs/options) except for key and fetcher.
+
+```typescript
+const { upload, data, error } = useFileUpload()
+
+upload(
+ {
+ provider: 'minio',
+ files: [new File([''], 'test.txt')],
+ },
+ {
+ throwOnError: false,
+ }
+)
+```
diff --git a/docs-website/src/pages/docs/clients-reference/typescript-client.md b/docs-website/src/pages/docs/clients-reference/typescript-client.md
new file mode 100644
index 000000000..69e082159
--- /dev/null
+++ b/docs-website/src/pages/docs/clients-reference/typescript-client.md
@@ -0,0 +1,248 @@
+---
+title: TypeScript Client
+pageTitle: WunderGraph TypeScript Client
+description: WunderGraph TypeScript Client reference
+---
+
+This is the base implementation of the WunderGraph HTTP protocol in TypeScript that can be used on both browser and server environments.
+It's used as the base interface for the Web client, React and Next.js implementations.
+
+## Configuration
+
+Let's start by adding the WunderGraph TypeScript client generator to your project.
+
+```typescript
+// wundergraph.config.ts
+
+configureWunderGraphApplication({
+ // ... your configuration
+ codeGenerators: [
+ {
+ templates: [templates.typescript.client],
+ path: '../generated',
+ },
+ ],
+})
+```
+
+## Create the client
+
+The generated `createClient` will return a fully typesafe client that can be used to execute operations.
+
+```ts
+import { createClient } from 'generated/client'
+
+const client = createClient()
+```
+
+## Client configuration
+
+### Custom baseURL
+
+The client can be configured with a custom baseURL.
+
+```ts
+const client = createClient({
+ baseURL: 'https://my-custom-base-url.com',
+})
+```
+
+### Node.js support
+
+You can use the client on server environments that don't have a build-in fetch implementation by using the customFetch configuration option.
+
+Install node-fetch or any other fetch polyfill.
+
+```bash
+npm i node-fetch
+```
+
+And add it to the client configuration.
+
+```ts
+import fetch from 'node-fetch'
+
+const client = createClient({
+ customFetch: fetch,
+})
+```
+
+### Browser
+
+If you target older browsers you will need a polyfill for fetch, AbortController, AbortSignal and possibly Promise.
+
+```ts
+import 'promise-polyfill/src/polyfill'
+import 'yet-another-abortcontroller-polyfill'
+import { fetch } from 'whatwg-fetch'
+
+const client = createClient({
+ customFetch: fetch,
+})
+```
+
+### Adding custom headers
+
+```ts
+const client = createClient({
+ extraHeaders: {
+ customHeader: 'value',
+ },
+})
+
+// or
+
+client.setExtraHeaders({
+ customHeader: 'value',
+})
+```
+
+## Methods
+
+### Run a query
+
+```ts
+const response = await client.query({
+ operationName: 'Hello',
+ input: {
+ hello: 'World',
+ },
+})
+```
+
+### Mutation
+
+```ts
+const response = await client.mutate({
+ operationName: 'SetName',
+ input: {
+ name: 'WunderGraph',
+ },
+})
+```
+
+### LiveQuery
+
+```ts
+client.subscribe(
+ {
+ operationName: 'Hello',
+ input: {
+ name: 'World',
+ },
+ liveQuery: true,
+ },
+ (response) => {}
+)
+```
+
+### Subscription
+
+```ts
+client.subscribe(
+ {
+ operationName: 'Countdown',
+ input: {
+ from: 100,
+ },
+ },
+ (response) => {}
+)
+```
+
+### One-of subscription
+
+You can run subscriptions using `subscribeOnce`, this will return the subscription response directly and will not setup a stream.
+This is useful for SSR purposes for example.
+
+```ts
+const response = await client.subscribe(
+ {
+ operationName: 'Countdown',
+ input: {
+ from: 100,
+ },
+ subscribeOnce: true,
+ },
+ (response) => {}
+)
+```
+
+### Upload files
+
+```ts
+const { fileKeys } = await client.uploadFiles({
+ provider: S3Provider.minio,
+ files,
+})
+```
+
+## Auth
+
+### Login
+
+```ts
+client.login('github')
+```
+
+### Log out
+
+```ts
+client.logout({
+ logoutOpenidConnectProvider: true,
+})
+```
+
+### Fetch user
+
+```ts
+const user = await client.fetchUser()
+```
+
+## AbortController
+
+Almost all methods accept an AbortController instance that can be used to cancel the request.
+
+```ts
+const controller = new AbortController()
+
+const { fileKeys } = await client.uploadFiles({
+ abortSignal: abortController.signal,
+ provider: S3Provider.minio,
+ files,
+})
+
+// cancel the request
+controller.abort()
+```
+
+## Error handling
+
+### Operations
+
+Query and mutation errors are returned as a `GraphQLResponseError` object. By default, the first error specifiy the error message but you can access all GraphQL errors through the `errors` property.
+Network errors and non 2xx responses are returned as a `ResponseError` object and contain the status code as `statusCode` property.
+
+```ts
+const { data, error } = await client.query({
+ operationName: 'Hello',
+ input: {
+ hello: 'World',
+ },
+})
+
+if (error instanceof GraphQLResponseError) {
+ error.errors[0].location
+} else if (error instanceof ResponseError) {
+ error.statusCode
+}
+```
+
+### Other
+
+Methods that initiate a network request throw a `ResponseError` or `Error` if the request fails to initiate or the response is not 2xx.
+You can be sure that the request was successful if the method doesn't throw an error.
+
+## Limitations
+
+- Subscriptions are not supported server side, but can be fetched using `subscribeOnce`.
diff --git a/docs-website/src/pages/docs/examples/nextjs-react-query.md b/docs-website/src/pages/docs/examples/nextjs-react-query.md
new file mode 100644
index 000000000..158af924d
--- /dev/null
+++ b/docs-website/src/pages/docs/examples/nextjs-react-query.md
@@ -0,0 +1,123 @@
+---
+title: Next.js + React Query Example
+pageTitle: WunderGraph - Examples - Next.js - React Query
+description:
+---
+
+[The NextJS example](https://github.com/wundergraph/wundergraph/tree/main/examples/nextjs-react-query) demonstrates the power of
+code generation,
+when it comes to integrating WunderGraph with frontend frameworks like Next.js.
+
+## Configuration
+
+Let's start by configuring WunderGraph.
+
+```typescript
+// wundergraph.config.ts
+const spaceX = introspect.graphql({
+ apiNamespace: 'spacex',
+ url: 'https://spacex-api.fly.dev/graphql/',
+})
+
+const myApplication = new Application({
+ name: 'app',
+ apis: [spaceX],
+})
+
+configureWunderGraphApplication({
+ application: myApplication,
+ server,
+ operations,
+ codeGenerators: [
+ {
+ templates: [
+ ...templates.typescript.all,
+ templates.typescript.operations,
+ templates.typescript.linkBuilder,
+ ],
+ },
+ {
+ templates: [templates.typescript.client],
+ path: '../components/generated',
+ },
+ ],
+})
+```
+
+What's notable here is that we're using `templates.typescript.client` to generate our base client that is used by the React Query [`@wundergraph/react-query`](https://github.com/wundergraph/wundergraph/tree/main/packages/react-query) package.
+
+## Define an Operation
+
+```graphql
+# .wundergraph/operations/Dragons.graphql
+query Dragons {
+ spacex_dragons {
+ name
+ active
+ }
+}
+```
+
+## Install the React Query Client
+
+```bash
+npm i @wundergraph/react-query @tanstack/react-query
+```
+
+Next up is setting up the React Query hooks.
+
+Create a new `.ts` file for example `lib/wundergraph.ts` and add the following code:
+
+```ts
+import { createClient, Operations } from '../generated/client'
+
+import { createHooks } from '@wundergraph/react-query'
+
+export const client = createClient()
+
+export const {
+ useQuery,
+ useMutation,
+ useSubscription,
+ useUser,
+ useAuth,
+ queryKey,
+} = createHooks(client)
+```
+
+## Configure your App
+
+```ts
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+
+const queryClient = new QueryClient()
+
+export default function App({ Component, pageProps }) {
+ return (
+
+
+
+ )
+}
+```
+
+## Running the Operation
+
+Now we're ready to run the operation. Edit `pages/index.tsx` and add the following code:
+
+```typescript
+import { NextPage } from 'next'
+import { useQuery, withWunderGraph } from '../components/generated/nextjs'
+
+export default function Home() {
+ const dragons = useQuery({ operationName: 'Dragons' })
+ return
{JSON.stringify(dragons.data)}
+}
+```
+
+That's it! You can now run the example with `npm run dev` and see the result in your browser.
+
+Learn more:
+
+- [@wundergraph/react-query reference](/docs/clients-reference/react-query)
+- [React Query documentation](https://tanstack.com/query/v4/docs/overview)
diff --git a/docs-website/src/pages/docs/examples/nextjs.md b/docs-website/src/pages/docs/examples/nextjs.md
index f9c4fb054..ffcd88400 100644
--- a/docs-website/src/pages/docs/examples/nextjs.md
+++ b/docs-website/src/pages/docs/examples/nextjs.md
@@ -25,14 +25,10 @@ configureWunderGraphApplication({
operations,
codeGenerators: [
{
- templates: [
- ...templates.typescript.all,
- templates.typescript.operations,
- templates.typescript.linkBuilder,
- ],
+ templates: [...templates.typescript.all],
},
{
- templates: [templates.typescript.client, new NextJsTemplate()],
+ templates: [new NextJsTemplate()],
path: '../components/generated',
},
],
@@ -76,3 +72,7 @@ so all you have to do is to import he `useQuery` hook and call your newly create
Wrapping the Page in `withWunderGraph` will make sure that Server Side Rendering (SSR) works,
that's it.
+
+## What's next
+
+Check out the [Next.js client documentation](/docs/clients-reference/nextjs) for more information.
diff --git a/examples/nextjs-react-query/.gitignore b/examples/nextjs-react-query/.gitignore
new file mode 100644
index 000000000..0bbe4a237
--- /dev/null
+++ b/examples/nextjs-react-query/.gitignore
@@ -0,0 +1,43 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+.vscode
+.idea
+
+# WunderGraph build output
+.wundergraph/cache
+.wundergraph/generated
+
+# 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*
+
+# local env files
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# vercel
+.vercel
+
+migrations
diff --git a/examples/nextjs-react-query/.graphqlconfig b/examples/nextjs-react-query/.graphqlconfig
new file mode 100644
index 000000000..5339585b7
--- /dev/null
+++ b/examples/nextjs-react-query/.graphqlconfig
@@ -0,0 +1,14 @@
+{
+ "project": {
+ "schemaPath": ".wundergraph/generated/wundergraph.schema.graphql",
+ "extensions": {
+ "endpoint": {
+ "introspect": false,
+ "url": "http://localhost:9991/graphql",
+ "headers": {
+ "user-agent": "WunderGraph Client"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/nextjs-react-query/.wundergraph/operations/Dragons.graphql b/examples/nextjs-react-query/.wundergraph/operations/Dragons.graphql
new file mode 100644
index 000000000..6ec1ad3fd
--- /dev/null
+++ b/examples/nextjs-react-query/.wundergraph/operations/Dragons.graphql
@@ -0,0 +1,7 @@
+query Dragons {
+ spacex_dragons {
+ name
+ active
+ id
+ }
+}
diff --git a/examples/nextjs-react-query/.wundergraph/wundergraph.config.ts b/examples/nextjs-react-query/.wundergraph/wundergraph.config.ts
new file mode 100644
index 000000000..2151edbae
--- /dev/null
+++ b/examples/nextjs-react-query/.wundergraph/wundergraph.config.ts
@@ -0,0 +1,38 @@
+import { authProviders, configureWunderGraphApplication, cors, introspect, templates } from '@wundergraph/sdk';
+import { NextJsTemplate } from '@wundergraph/nextjs/dist/template';
+import server from './wundergraph.server';
+import operations from './wundergraph.operations';
+
+const spaceX = introspect.graphql({
+ apiNamespace: 'spacex',
+ url: 'https://spacex-api.fly.dev/graphql/',
+});
+
+// configureWunderGraph emits the configuration
+configureWunderGraphApplication({
+ apis: [spaceX],
+ server,
+ operations,
+ codeGenerators: [
+ {
+ templates: [...templates.typescript.all, templates.typescript.operations, templates.typescript.linkBuilder],
+ },
+ {
+ templates: [templates.typescript.client, new NextJsTemplate()],
+ path: '../components/generated',
+ },
+ ],
+ cors: {
+ ...cors.allowAll,
+ allowedOrigins: process.env.NODE_ENV === 'production' ? ['http://localhost:3000'] : ['http://localhost:3000'],
+ },
+ authentication: {
+ cookieBased: {
+ providers: [authProviders.demo()],
+ authorizedRedirectUris: ['http://localhost:3000'],
+ },
+ },
+ security: {
+ enableGraphQLEndpoint: process.env.NODE_ENV !== 'production',
+ },
+});
diff --git a/examples/nextjs-react-query/.wundergraph/wundergraph.operations.ts b/examples/nextjs-react-query/.wundergraph/wundergraph.operations.ts
new file mode 100644
index 000000000..47ccae4b1
--- /dev/null
+++ b/examples/nextjs-react-query/.wundergraph/wundergraph.operations.ts
@@ -0,0 +1,32 @@
+import { configureWunderGraphOperations } from '@wundergraph/sdk';
+import type { OperationsConfiguration } from './generated/wundergraph.operations';
+
+export default configureWunderGraphOperations({
+ operations: {
+ defaultConfig: {
+ authentication: {
+ required: false,
+ },
+ },
+ queries: (config) => ({
+ ...config,
+ caching: {
+ enable: false,
+ staleWhileRevalidate: 60,
+ maxAge: 60,
+ public: true,
+ },
+ liveQuery: {
+ enable: true,
+ pollingIntervalSeconds: 1,
+ },
+ }),
+ mutations: (config) => ({
+ ...config,
+ }),
+ subscriptions: (config) => ({
+ ...config,
+ }),
+ custom: {},
+ },
+});
diff --git a/examples/nextjs-react-query/.wundergraph/wundergraph.server.ts b/examples/nextjs-react-query/.wundergraph/wundergraph.server.ts
new file mode 100644
index 000000000..3c636728e
--- /dev/null
+++ b/examples/nextjs-react-query/.wundergraph/wundergraph.server.ts
@@ -0,0 +1,12 @@
+import { GraphQLObjectType, GraphQLSchema, GraphQLString } from 'graphql';
+import { configureWunderGraphServer } from '@wundergraph/sdk';
+import type { HooksConfig } from './generated/wundergraph.hooks';
+import type { InternalClient } from './generated/wundergraph.internal.client';
+
+export default configureWunderGraphServer(() => ({
+ hooks: {
+ queries: {},
+ mutations: {},
+ },
+ graphqlServers: [],
+}));
diff --git a/examples/nextjs-react-query/README.md b/examples/nextjs-react-query/README.md
new file mode 100644
index 000000000..245724431
--- /dev/null
+++ b/examples/nextjs-react-query/README.md
@@ -0,0 +1,27 @@
+# WunderGraph Next.js React Query Starter
+
+This example demonstrates how to use WunderGraph with Next.js and React Query. We are going to make your data-source accessible through JSON-RPC to your Next.js app.
+
+## Getting Started
+
+Install the dependencies and run the complete example in one command:
+
+```shell
+npm install && npm start
+```
+
+After a while, a new browser tab will open,
+and you can start exploring the application.
+If no tab is open, navigate to [http://localhost:3000](http://localhost:3000).
+
+Running WunderGraph will automatically introspect the data-source and generate an API for you.
+You can add more Operations (e.g. Queries or Mutations) by adding more "\*.graphql" files to the directory `./wundergraph/operations`.
+Each file becomes an Operation. The Operation name is not relevant, the file name is.
+
+## Learn More
+
+Read the [Docs](https://wundergraph.com/docs).
+
+## Got Questions?
+
+Join us on [Discord](https://wundergraph.com/discord)!
diff --git a/examples/nextjs-react-query/components/Nav.tsx b/examples/nextjs-react-query/components/Nav.tsx
new file mode 100644
index 000000000..4901d165a
--- /dev/null
+++ b/examples/nextjs-react-query/components/Nav.tsx
@@ -0,0 +1,14 @@
+const NavBar = () => {
+ return (
+
+ );
+};
+export default NavBar;
diff --git a/examples/nextjs-react-query/lib/react-query.ts b/examples/nextjs-react-query/lib/react-query.ts
new file mode 100644
index 000000000..8cd8e022f
--- /dev/null
+++ b/examples/nextjs-react-query/lib/react-query.ts
@@ -0,0 +1,6 @@
+import { createHooks } from '@wundergraph/react-query';
+import { createClient, Operations } from '../components/generated/client';
+const client = createClient(); // Typesafe WunderGraph client
+
+export const { useQuery, useMutation, useSubscription, useUser, useFileUpload, useAuth, queryKey } =
+ createHooks(client);
diff --git a/examples/nextjs-react-query/next-env.d.ts b/examples/nextjs-react-query/next-env.d.ts
new file mode 100644
index 000000000..4f11a03dc
--- /dev/null
+++ b/examples/nextjs-react-query/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/examples/nextjs-react-query/next.config.js b/examples/nextjs-react-query/next.config.js
new file mode 100644
index 000000000..5ee7a35ec
--- /dev/null
+++ b/examples/nextjs-react-query/next.config.js
@@ -0,0 +1,8 @@
+const path = require('path');
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+ swcMinify: true,
+};
+
+module.exports = nextConfig;
diff --git a/examples/nextjs-react-query/package.json b/examples/nextjs-react-query/package.json
new file mode 100644
index 000000000..3f03e81f2
--- /dev/null
+++ b/examples/nextjs-react-query/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "wundergraph-nextjs",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "start": "run-p dev wundergraph open",
+ "wundergraph": "wunderctl up --debug",
+ "open": "wait-on -d 500 http://localhost:9991 && open-cli http://localhost:3000",
+ "build": "next build",
+ "dev": "next dev",
+ "check": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@tanstack/react-query": "^4.16.1",
+ "@wundergraph/nextjs": "^0.5.0",
+ "@wundergraph/react-query": "^0.1.0",
+ "@wundergraph/sdk": "^0.123.0",
+ "graphql": "^16.3.0",
+ "next": "^12.1.6",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "devDependencies": {
+ "@types/node": "^17.0.15",
+ "@types/react": "^18.0.6",
+ "autoprefixer": "^10.4.7",
+ "node-fetch": "^2.6.7",
+ "npm-run-all": "^4.1.5",
+ "open-cli": "^7.0.1",
+ "postcss": "^8.4.19",
+ "tailwindcss": "^3.1.4",
+ "ts-node": "^10.8.0",
+ "typescript": "^4.8.2",
+ "wait-on": "^6.0.1"
+ }
+}
diff --git a/examples/nextjs-react-query/pages/_app.tsx b/examples/nextjs-react-query/pages/_app.tsx
new file mode 100644
index 000000000..7d4cce0e9
--- /dev/null
+++ b/examples/nextjs-react-query/pages/_app.tsx
@@ -0,0 +1,21 @@
+import Head from 'next/head';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+function MyApp({ Component, pageProps }) {
+ const queryClient = new QueryClient();
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+export default MyApp;
diff --git a/examples/nextjs-react-query/pages/index.tsx b/examples/nextjs-react-query/pages/index.tsx
new file mode 100644
index 000000000..56f9aff94
--- /dev/null
+++ b/examples/nextjs-react-query/pages/index.tsx
@@ -0,0 +1,64 @@
+import { NextPage } from 'next';
+import { useQuery } from '../lib/react-query';
+import Nav from '../components/Nav';
+
+const Home: NextPage = () => {
+ const dragons = useQuery({
+ operationName: 'Dragons',
+ });
+
+ return (
+
+ );
+};
+
+export default Home;
diff --git a/examples/nextjs-react-query/postcss.config.js b/examples/nextjs-react-query/postcss.config.js
new file mode 100644
index 000000000..e873f1a4f
--- /dev/null
+++ b/examples/nextjs-react-query/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/examples/nextjs-react-query/public/favicon.ico b/examples/nextjs-react-query/public/favicon.ico
new file mode 100644
index 000000000..4965832f2
Binary files /dev/null and b/examples/nextjs-react-query/public/favicon.ico differ
diff --git a/examples/nextjs-react-query/tailwind.config.js b/examples/nextjs-react-query/tailwind.config.js
new file mode 100644
index 000000000..3a182af4c
--- /dev/null
+++ b/examples/nextjs-react-query/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+};
diff --git a/examples/nextjs-react-query/tsconfig.json b/examples/nextjs-react-query/tsconfig.json
new file mode 100644
index 000000000..2bfacdc5e
--- /dev/null
+++ b/examples/nextjs-react-query/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true
+ },
+ "include": ["next-env.d.ts", "**/*.ts", ".wundergraph/**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"],
+ "ts-node": {
+ "compilerOptions": {
+ "module": "commonjs"
+ }
+ }
+}
diff --git a/examples/vite-swr/readme.md b/examples/vite-swr/readme.md
new file mode 100644
index 000000000..fecb8450a
--- /dev/null
+++ b/examples/vite-swr/readme.md
@@ -0,0 +1,31 @@
+# WunderGraph Vite + SWR Starter
+
+This example demonstrates how to use WunderGraph with [Vite](https://vitejs.dev/) and [SWR](https://swr.vercel.app).
+
+Vite is a new build tool that aims to provide a faster and leaner development experience for modern web projects. It is built on top of [Rollup](https://rollupjs.org/guide/en/) and [esbuild](https://esbuild.github.io/).
+
+SWR is a React library for data fetching. With just one hook, you can significantly simplify the data fetching logic in your project. And it also covered in all aspects of speed, correctness, and stability to help you build better experiences.
+
+## Getting Started
+
+Install the dependencies and run the complete example in one command:
+
+```shell
+npm install && npm run dev
+```
+
+After a while, a new browser tab will open,
+and you can start exploring the application.
+If no tab is open, navigate to [http://localhost:5173](http://localhost:5173).
+
+Running WunderGraph will automatically introspect the data-source and generate an API for you.
+You can add more Operations (e.g. Queries or Mutations) by adding more "\*.graphql" files to the directory `./wundergraph/operations`.
+Each file becomes an Operation. The Operation name is not relevant, the file name is.
+
+## Learn More
+
+Read the [Docs](https://wundergraph.com/docs).
+
+## Got Questions?
+
+Join us on [Discord](https://wundergraph.com/discord)!
diff --git a/packages/nextjs/README.md b/packages/nextjs/README.md
index a02bac9ab..d158c3ea2 100644
--- a/packages/nextjs/README.md
+++ b/packages/nextjs/README.md
@@ -9,7 +9,7 @@ WunderGraph codegen template plugin to add deep Next.js integration.
## Getting Started
```shell
-npm install @wundergraph/nextjs
+npm install @wundergraph/nextjs swr@2.0.0-rc.0
```
### 1. Register the codegen template
@@ -33,13 +33,21 @@ configureWunderGraphApplication({
```tsx
// pages/authentication.ts
-import { useQuery, useMutation, useLiveQuery, AuthProviders } from '.wundergraph/generated/nextjs';
+import {
+ withWunderGraph,
+ useQuery,
+ useMutation,
+ useSubscription,
+ useAuth,
+ useUser,
+} from '.wundergraph/generated/nextjs';
const Example: ExamplePage = () => {
- const { user, login, logout } = useWunderGraph();
+ const { login, logout } = useAuth();
+ const { data: user } = useUser();
const onClick = () => {
if (user === null || user === undefined) {
- login(AuthProviders.github);
+ login('github');
} else {
logout();
}
diff --git a/packages/react-query/.npmignore b/packages/react-query/.npmignore
new file mode 100644
index 000000000..92f017677
--- /dev/null
+++ b/packages/react-query/.npmignore
@@ -0,0 +1,5 @@
+node_modules
+src
+tsconfig.json
+.gitignore
+dist/tsconfig.tsbuildinfo
diff --git a/packages/react-query/CHANGELOG.md b/packages/react-query/CHANGELOG.md
new file mode 100644
index 000000000..e4d87c4d4
--- /dev/null
+++ b/packages/react-query/CHANGELOG.md
@@ -0,0 +1,4 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/packages/react-query/README.md b/packages/react-query/README.md
new file mode 100644
index 000000000..be49c512e
--- /dev/null
+++ b/packages/react-query/README.md
@@ -0,0 +1,157 @@
+# WunderGraph React Query Integration
+
+![wunderctl](https://img.shields.io/npm/v/@wundergraph/react-query.svg)
+
+This package provides a type-safe integration of [React Query](https://tanstack.com/query/v4/docs/overview) with WunderGraph.
+React Query is a data fetching library for React. With just one hook, you can significantly simplify the data fetching logic in your project. And it also covered in all aspects of speed, correctness, and stability to help you build better experiences.
+
+> **Warning**: Only works with WunderGraph.
+
+## Getting Started
+
+```shell
+npm install @wundergraph/react-query @tanstack/react-query
+```
+
+Before you can use the hooks, you need to modify your code generation to include the base typescript client.
+
+```typescript
+// wundergraph.config.ts
+configureWunderGraphApplication({
+ // ... omitted for brevity
+ codeGenerators: [
+ {
+ templates: [templates.typescript.client],
+ // the location where you want to generate the client
+ path: '../src/components/generated',
+ },
+ ],
+});
+```
+
+Second, run `wunderctl generate` to generate the code.
+
+Now you can configure the hooks. Create a new file, for example `lib/wundergraph.ts` and add the following code:
+
+```ts
+import { createHooks } from '@wundergraph/react-query';
+import { createClient, Operations } from './components/generated/client';
+
+const client = createClient(); // Typesafe WunderGraph client
+
+export const { useQuery, useMutation, useSubscription, useUser, useFileUpload, useAuth } =
+ createHooks(client);
+```
+
+In your `App.tsx` add QueryClientProvider:
+
+```tsx
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+const queryClient = new QueryClient();
+
+export default App() {
+ return (
+
+
+ This Example looks similar to Realtime Subscriptions with one exception, users have to be authenticated to be
+ able to use this Operation.
+
+
Click the Login Button to login using GitHub. We provide a GitHub demo Oauth2 account for this to work.
+
While not logged in, the LiveQuery will reset and wait until the user is logged in.
+
Once you logged in, the LiveQuery will start streaming until you de-focus the browser tab or log out.
+
+ When you re-focus the tab, the LiveQuery will start streaming again. You can open the DevTools / Network tab to
+ observe this.
+
+
+ Next, you can try something. First, make sure you're logged in. Then open a second tab and open the same url.
+ Log yourself out in the second tab. Then come back to the first tab and see what happens once you focus the tab.
+ =)
+
+ Have a look at the Network Tab / Devtools, the response is cached up to 120 seconds whereas after 60 seconds,
+ the Browser cache will be invalidated through stale while revalidate.
+
+
+ Cache- & revalidation timing can be edited in{' '}
+ .wundergraph/wundergraph.operations.ts:36
+
+
+ Caching is enabled in .wundergraph/wundergraph.operations.ts:55
+
+
+ The use of the generated client can be found at pages/caching.tsx:6
+
+
+ Additionally, if you re-focus the Browser window/tab you'll see a network request kick off to refresh the page.
+
+ If you watch the Network Tab / DevTools, you'll see no WebSockets, No Subscriptions, No Polling, just a GET
+ request with chunked encoding (HTTP 1.1) or a stream (HTTP/2).
+
+
If you blur (un-focus) the browser window/tab you'll see that the stream ends.
+
Once you re-enter the window, the stream re-starts and keepts the UI updated.
+
+ The upstream doesn't support Subscriptions or Realtime Updates. WunderGraph polls the upstream on the serverside
+ and distributed the response to the clients.
+
+
+ You can change the polling interval by adjusting the "liveQuery" config in at{' '}
+ .wundergraph/wundergraph.operations.ts:44
+