nextjs-hasura-auth 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/APOLLO.md CHANGED
@@ -1,52 +1,91 @@
1
1
  # Apollo Client Setup (`APOLLO.md`)
2
2
 
3
- This document describes the configured Apollo Client instance used in this project (`lib/apolloClient.ts` or similar) for interacting with the Hasura GraphQL API.
3
+ This document describes the configured Apollo Client instance provided by this package (`lib/apollo.tsx` or similar) for interacting with your Hasura GraphQL API.
4
4
 
5
5
  ## Purpose
6
6
 
7
- The Apollo Client setup provides a unified interface for sending GraphQL queries, mutations, and subscriptions to Hasura, automatically handling authentication based on the user's session.
7
+ The Apollo Client setup provides a unified interface for sending GraphQL queries, mutations, and subscriptions to Hasura, automatically handling authentication based on the provided JWT token or Hasura Admin Secret.
8
8
 
9
9
  ## Key Features
10
10
 
11
+ * **Direct Connection:** Connects directly to your Hasura GraphQL endpoint (`NEXT_PUBLIC_HASURA_GRAPHQL_URL`) for HTTP requests and WebSocket endpoint (`NEXT_PUBLIC_HASURA_WS_ENDPOINT` derived from the HTTP URL) for subscriptions.
11
12
  * **Split Link:** Intelligently routes GraphQL operations:
12
- * **HTTP Requests (Queries/Mutations):** Sent via a secure Next.js API route (`/api/graphql-proxy` or equivalent). This proxy uses the user's session JWT to make authenticated requests to Hasura using the `HASURA_ADMIN_SECRET`, keeping the admin secret safe.
13
- * **WebSocket Connections (Subscriptions):** Establishes a direct WebSocket connection to the Hasura endpoint (`NEXT_PUBLIC_HASURA_WS_ENDPOINT`).
14
- * **Automatic Authentication:**
15
- * **HTTP:** The proxy handles injecting appropriate `X-Hasura-*` headers based on the user's session JWT.
16
- * **WS:** The WebSocket link automatically includes the user's session JWT in the `connectionParams` for Hasura to authenticate the connection.
17
- * **Server-Side Rendering (SSR) / Static Site Generation (SSG) Compatibility:** The client can be initialized and used server-side for pre-fetching data.
18
- * **Client-Side Usage:** Seamless integration with Apollo Client's React hooks (`useQuery`, `useMutation`, `useSubscription`).
13
+ * **HTTP Requests (Queries/Mutations):** Sent via standard HTTP.
14
+ * **WebSocket Connections (Subscriptions):** Uses a WebSocket connection (`graphql-ws`). This requires `ws: true` during client creation and only works client-side.
15
+ * **Flexible Authentication:**
16
+ * **JWT Token Priority:** If a `token` is provided during client creation, it's sent as a `Bearer` token in the `Authorization` header (for both HTTP and WS).
17
+ * **Admin Secret Fallback:** If no `token` is provided, but the `HASURA_ADMIN_SECRET` environment variable is set (or a `secret` is passed explicitly), it's sent in the `x-hasura-admin-secret` header (for both HTTP and WS).
18
+ * **Public Access:** If neither a token nor a secret is available, requests are sent without authentication headers.
19
+ * **Server-Side Rendering (SSR) / Client-Side Rendering (CSR) Compatibility:** The client can be created and used both on the server and the client.
20
+ * **Convenient Provider:** The `createApolloClient` function returns an enhanced client instance that includes a `.Provider` component (`ApolloClientWithProvider`) for easily wrapping parts of your React application.
21
+ * **Helper Hook:** Provides `useCreateApolloClient` hook for easily creating memoized client instances in React components, especially useful for integrating with authentication state.
22
+
23
+ <details>
24
+ <summary>Core Exports & Options (`lib/apollo.tsx`)</summary>
25
+
26
+ * `createApolloClient(options: ApolloOptions): ApolloClientWithProvider`: Creates a new Apollo Client instance.
27
+ * `options.url`: Hasura GraphQL endpoint URL (defaults to `process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL`).
28
+ * `options.ws`: Boolean, enable WebSocket link for subscriptions (defaults to `false`, only works client-side).
29
+ * `options.token`: String, JWT token for Bearer authentication.
30
+ * `options.secret`: String, Hasura Admin Secret (defaults to `process.env.HASURA_ADMIN_SECRET`).
31
+ * `useCreateApolloClient(options: ApolloOptions): ApolloClientWithProvider`: React hook to create a memoized Apollo Client instance. Recreates the client if `options` change.
32
+ * `getClient(options?: ApolloOptions): ApolloClient<any>`: Gets or creates a singleton Apollo Client instance. *Note: Using the singleton pattern might be tricky if the authentication token needs to change dynamically. `useCreateApolloClient` is often preferred in React components.*
33
+ * `checkConnection(client?): Promise<boolean>`: Sends a simple introspection query to check connectivity to the Hasura endpoint.
34
+ * `getJwtSecret(): Uint8Array`: Utility to get the Hasura JWT secret key from `process.env.HASURA_JWT_SECRET` (used internally by other parts of the auth system, not typically needed directly for Apollo client setup).
35
+
36
+ </details>
19
37
 
20
38
  ## Usage
21
39
 
22
- ### Client-Side (React Components)
40
+ ### Client-Side (React Components - Recommended)
23
41
 
24
- 1. **Wrap your application (or relevant layout) with `ApolloProvider`:**
25
- This is typically done in your root layout file (`app/layout.tsx` or `pages/_app.tsx`). You'll need a way to get the initialized Apollo Client instance.
42
+ 1. **Ensure `SessionProvider` is set up:** The Apollo client often relies on session data (like a JWT) for authentication. Make sure `next-auth/react`'s `SessionProvider` wraps your application, usually in the root layout.
43
+ 2. **Wrap your application/layout with the client's Provider:** Use the `useCreateApolloClient` hook to create a client instance based on the session state and wrap your components using `client.Provider`.
26
44
 
27
45
  ```typescript
28
- // Example in app/layout.tsx (simplified)
29
- 'use client' // ApolloProvider likely requires client-side context
46
+ // Example in app/layout.tsx
47
+ 'use client'; // Layout needs to be client-side for providers and hooks
48
+
49
+ import { SessionProvider, useSession } from "next-auth/react";
50
+ import { useCreateApolloClient } from '@/lib/apollo'; // Adjust path
51
+ import { useMemo } from "react";
52
+ // Other imports (ThemeProvider, etc.)
53
+
54
+ // Wrapper component to manage Apollo client based on session
55
+ function ApolloWrapper({ children }: { children: React.ReactNode }) {
56
+ const { data: session } = useSession(); // Get session from next-auth
57
+
58
+ // Create Apollo Client, passing the Hasura JWT from the session
59
+ // The client will be recreated if the session changes
60
+ const client = useCreateApolloClient(useMemo(() => ({
61
+ // Make sure your next-auth session callback adds the correct
62
+ // Hasura-compatible JWT to the session object (e.g., session.accessToken)
63
+ token: session?.accessToken, // Pass the Hasura JWT
64
+ ws: true // Enable WebSocket support for subscriptions
65
+ }), [session]));
30
66
 
31
- import { ApolloProvider } from '@apollo/client';
32
- import { getClient } from '../lib/apolloClient'; // Adjust path as needed
33
- import { SessionProvider } from 'next-auth/react'; // Needed for hooks to get session token
67
+ // Use the convenient Provider attached to the client instance
68
+ return (
69
+ <client.Provider>
70
+ {children}
71
+ </client.Provider>
72
+ );
73
+ }
34
74
 
35
75
  export default function RootLayout({ children }: { children: React.ReactNode }) {
36
- // Create Apollo Client using the token from the session if it exists
37
- const client = useClient(useMemo(() => ({
38
- token: session?.accessToken, // Pass Hasura token from session
39
- ws: true // Enable WebSocket support
40
- }), [session]));
41
-
42
76
  return (
43
- <html lang="en">
77
+ <html lang="en" suppressHydrationWarning>
78
+ <head />
44
79
  <body>
45
- {/* SessionProvider needed for auth state access */}
80
+ {/* SessionProvider is required for useSession hook */}
46
81
  <SessionProvider>
47
- <ApolloProvider client={client}>
48
- {children}
49
- </ApolloProvider>
82
+ {/* ApolloWrapper provides the Apollo client based on session */}
83
+ <ApolloWrapper>
84
+ {/* Other providers like ThemeProvider */}
85
+ {/* <ThemeProvider ...> */}
86
+ {children}
87
+ {/* </ThemeProvider> */}
88
+ </ApolloWrapper>
50
89
  </SessionProvider>
51
90
  </body>
52
91
  </html>
@@ -54,26 +93,16 @@ The Apollo Client setup provides a unified interface for sending GraphQL queries
54
93
  }
55
94
  ```
56
95
 
57
- 2. **Use Apollo Hooks in Components:**
58
- Import and use hooks like `useQuery`, `useMutation`, or `useSubscription` directly in your components. Combine with the `Generator` for dynamic queries (see [`GENERATOR.md`](GENERATOR.md)).
96
+ 3. **Use Apollo Hooks in Components:** Import and use hooks like `useQuery`, `useMutation`, or `useSubscription` directly. The client provided by the context will handle authentication automatically based on the token passed during its creation.
59
97
 
60
98
  ```typescript
61
- import { useQuery } from '@apollo/client';
62
- import { Generator, GenerateOptions } from 'nextjs-hasura-auth'; // Assuming Generator is exported
63
- // import schema from '...'; // Load schema
64
-
65
- // const generate = Generator(schema);
66
-
67
- function UserProfile({ userId }) {
68
- // Example using Generator (assuming you have generate function)
69
- // const { query, variables } = generate({
70
- // operation: 'query',
71
- // table: 'users',
72
- // pk_columns: { id: userId },
73
- // returning: ['id', 'name', 'email']
74
- // });
99
+ import { useQuery, gql } from '@apollo/client';
100
+ // import { Generator } from 'nextjs-hasura-auth'; // Optional: Use with Generator
101
+
102
+ function UserProfile() {
103
+ // Assume user ID is available, e.g., from session or props
104
+ const userId = '...';
75
105
 
76
- // Or use a manually written query
77
106
  const GET_USER = gql`
78
107
  query GetUser($userId: uuid!) {
79
108
  users_by_pk(id: $userId) {
@@ -83,46 +112,50 @@ The Apollo Client setup provides a unified interface for sending GraphQL queries
83
112
  }
84
113
  }
85
114
  `;
115
+
116
+ // The client from ApolloWrapper's context is used automatically
86
117
  const { loading, error, data } = useQuery(GET_USER, {
87
118
  variables: { userId }
88
119
  });
89
120
 
90
- if (loading) return <p>Loading...</p>;
91
- if (error) return <p>Error: {error.message}</p>;
92
-
93
- return (
94
- <div>
95
- <h1>{data.users_by_pk.name}</h1>
96
- <p>{data.users_by_pk.email}</p>
97
- </div>
98
- );
121
+ // ... render logic ...
99
122
  }
100
123
  ```
101
124
 
102
- ### Server-Side (SSR/SSG/Server Components)
125
+ ### Server-Side (SSR/SSG/Server Components/API Routes)
103
126
 
104
- 1. **Get Client Instance:** Obtain the Apollo Client instance, potentially re-initializing it for server-side scope if necessary.
105
- 2. **Fetch Data:** Use `client.query(...)` or `client.mutate(...)` directly.
127
+ 1. **Create Client Instance:** Use `createApolloClient` directly. You need to decide how to authenticate:
128
+ * **Admin Access:** Pass the admin secret (e.g., from environment variables) using the `secret` option if you need privileged access.
129
+ * **User Access:** If running in the context of a specific user request, obtain their Hasura JWT (e.g., from session data, request headers) and pass it using the `token` option.
106
130
 
107
131
  ```typescript
108
- // Example in a Server Component or getStaticProps/getServerSideProps
109
- import { getClient } from '../lib/apolloClient'; // Adjust path
110
- // import { Generator } from 'nextjs-hasura-auth';
111
- // import schema from '...';
112
- // const generate = Generator(schema);
132
+ // Example in an API Route or Server Component
133
+ import { createApolloClient } from '@/lib/apollo'; // Adjust path
134
+ import { gql } from '@apollo/client';
135
+ // import { getToken } from "next-auth/jwt"
136
+
137
+ async function getUserDataOnServer(userId: string, request?: Request) {
138
+ // Option 1: Use Admin Secret for privileged access
139
+ // const client = createApolloClient({
140
+ // secret: process.env.HASURA_ADMIN_SECRET // Ensure secret is available server-side
141
+ // });
113
142
 
114
- async function getUserData(userId) {
115
- const client = getClient(); // Get client instance
143
+ // Option 2: Use User Token (if available from request/session)
144
+ // Example: const sessionToken = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET });
145
+ // const hasuraToken = sessionToken?.accessToken;
146
+ const client = createApolloClient({
147
+ token: process.env.SOME_SERVICE_ACCOUNT_TOKEN_IF_NEEDED // Or fetch user token if applicable
148
+ // url: process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL // URL can also be passed explicitly
149
+ });
116
150
 
117
- // const { query, variables } = generate({ ... });
118
151
  const GET_USER = gql`...`; // Your query
119
152
 
120
153
  try {
121
154
  const { data } = await client.query({
122
- query: GET_USER, // Use generated or manual query
155
+ query: GET_USER,
123
156
  variables: { userId },
124
- // Ensure proper fetch policy for server-side needs
125
- fetchPolicy: 'network-only',
157
+ // Important for server-side: Avoid using client-side cache
158
+ fetchPolicy: 'network-only',
126
159
  });
127
160
  return data.users_by_pk;
128
161
  } catch (error) {
@@ -132,17 +165,10 @@ The Apollo Client setup provides a unified interface for sending GraphQL queries
132
165
  }
133
166
  ```
134
167
 
135
- ## Configuration (`lib/apolloClient.ts`)
136
-
137
- The core logic resides in `lib/apolloClient.ts` (or a similar file). It typically involves:
168
+ 2. **Fetch Data:** Use `client.query(...)` or `client.mutate(...)`.
138
169
 
139
- 1. **Creating an `HttpLink`:** Points to the Next.js GraphQL proxy (e.g., `/api/graphql-proxy`).
140
- 2. **Creating a `WebSocketLink`:**
141
- * Uses the `graphql-ws` library.
142
- * Connects to `NEXT_PUBLIC_HASURA_WS_ENDPOINT`.
143
- * Configures `connectionParams` to dynamically fetch the session JWT (e.g., using `getSession()` from `next-auth/react` or a similar mechanism) and include it as `{ headers: { Authorization: Bearer <token> } }`.
144
- 3. **Using `split`:** Combines the HTTP and WebSocket links, directing operations based on their type (queries/mutations vs. subscriptions).
145
- 4. **Creating the `ApolloClient` instance:** Initializes the client with the combined link and a cache (e.g., `InMemoryCache`).
146
- 5. **Handling Singleton Pattern:** Often uses a singleton pattern to avoid creating multiple client instances, especially on the server.
170
+ ## Important Considerations
147
171
 
148
- *(Refer to the actual `lib/apolloClient.ts` file for the precise implementation details.)*
172
+ * **JWT Token:** Ensure your authentication system (like `next-auth`) generates a Hasura-compatible JWT containing the necessary `https://hasura.io/jwt/claims` and includes it in the session data (e.g., as `session.accessToken`) for the client-side integration to work.
173
+ * **Environment Variables:** Securely manage `HASURA_ADMIN_SECRET`, `NEXTAUTH_SECRET`, and potentially `HASURA_JWT_SECRET` using environment variable configuration in your hosting provider (e.g., Vercel). `NEXT_PUBLIC_HASURA_GRAPHQL_URL` needs to be accessible client-side.
174
+ * **WebSocket Security:** WebSocket connections initiated from the client include the token/secret directly in the `connectionParams`. Ensure your Hasura endpoint is properly secured (e.g., using HTTPS/WSS).
package/GENERATOR.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- This document describes the `Generator` function located in `lib/generator.ts`. Its purpose is to dynamically generate GraphQL query strings, `DocumentNode` objects (compatible with Apollo Client), and corresponding variables based on a provided set of options and a Hasura-like schema (`schema.json`). This simplifies the process of constructing GraphQL operations (queries, mutations, subscriptions) within the application.
5
+ This document describes the `Generator` function located in `lib/generator.ts`. Its purpose is to dynamically generate GraphQL query strings, `DocumentNode` objects (compatible with Apollo Client), and corresponding variables based on a provided set of options and a Hasura-like schema (`public/hasura-schema.json`). This simplifies the process of constructing GraphQL operations (queries, mutations, subscriptions) within the application.
6
6
 
7
7
  ## Usage
8
8
 
@@ -14,11 +14,10 @@ This document describes the `Generator` function located in `lib/generator.ts`.
14
14
  // Assuming 'nextjs-hasura-auth' is your published package name
15
15
  import { Generator, GenerateOptions, GenerateResult } from 'nextjs-hasura-auth';
16
16
  // Assuming schema is correctly loaded (you might need to handle schema loading differently when using the package)
17
- // import schema from './schema.json';
17
+ import schema from './public/hasura-schema.json';
18
18
 
19
19
  // Initialize the generator (Schema needs to be passed)
20
- // const generate = Generator(schema);
21
- // TODO: Document how schema should be provided when using the package
20
+ const generate = Generator(schema);
22
21
 
23
22
  // Define options for your query
24
23
  const options: GenerateOptions = {
@@ -77,7 +76,7 @@ The `generate` function accepts an object with the following properties:
77
76
 
78
77
  ## Examples
79
78
 
80
- *(Note: Variable types like `users_bool_exp`, `[users_order_by!]`, `uuid!`, `users_pk_columns_input!`, `users_set_input!`, `[users_insert_input!]!` are inferred based on schema structure and conventions. Ensure your `schema.json` reflects the actual types used by Hasura.)*
79
+ *(Note: Variable types like `users_bool_exp`, `[users_order_by!]`, `uuid!`, `users_pk_columns_input!`, `users_set_input!`, `[users_insert_input!]!` are inferred based on schema structure and conventions. Ensure your `public/hasura-schema.json` reflects the actual types used by Hasura.)*
81
80
 
82
81
  **Navigation**
83
82
 
package/README.md CHANGED
@@ -3,11 +3,19 @@
3
3
  This project provides a robust starting point for building applications using Next.js (App Router), Hasura, and strong authentication patterns. It features JWT-based authentication with NextAuth.js, a secure GraphQL proxy to Hasura, direct WebSocket support for subscriptions, and a powerful dynamic query generator.
4
4
 
5
5
  [![Generator Documentation](https://img.shields.io/badge/Generator%20Docs-MD-blue)](GENERATOR.md) [![Apollo Client Documentation](https://img.shields.io/badge/Apollo%20Client%20Docs-MD-orange)](APOLLO.md)
6
+ [![Authentication Helpers Documentation](https://img.shields.io/badge/Auth%20Helpers%20Docs-MD-green)](AUTH.md) [![Hasura Admin Client Documentation](https://img.shields.io/badge/Hasura%20Client%20Docs-MD-purple)](HASURA.md)
7
+ [![Generated Client Documentation](https://img.shields.io/badge/Generated%20Client%20Docs-MD-cyan)](CLIENT.md)
6
8
 
7
9
  See [`GENERATOR.md`](GENERATOR.md) for detailed documentation on the dynamic GraphQL query generator, which simplifies creating queries, mutations, and subscriptions based on your Hasura schema.
8
10
 
9
11
  See [`APOLLO.md`](APOLLO.md) for details on the configured Apollo Client instance and how it handles authenticated requests and subscriptions.
10
12
 
13
+ See [`AUTH.md`](AUTH.md) for documentation on WebSocket and request authentication helpers.
14
+
15
+ See [`HASURA.md`](HASURA.md) for details on the Hasura Admin API client used for migrations.
16
+
17
+ See [`CLIENT.md`](CLIENT.md) for details on the `Client` class and React hooks that combine the Generator and Apollo Client for easy data operations.
18
+
11
19
  ## ✨ Features Checklist
12
20
 
13
21
  **Implemented:**
@@ -17,16 +25,17 @@ See [`APOLLO.md`](APOLLO.md) for details on the configured Apollo Client instanc
17
25
  * [x] **Unified Apollo Client:** A configured Apollo Client instance handles both authenticated HTTP requests (via the proxy) and direct, authenticated WebSocket connections for subscriptions.
18
26
  * [x] **Dynamic Query Generator:** A versatile query generator (`lib/generator.ts`) allows dynamic creation of GraphQL operations based on options and schema, suitable for client/server use.
19
27
  * [x] **WebSocket Authentication:** Real-time subscriptions connect directly to Hasura via WebSockets, authenticated using the user's session JWT.
28
+ * [x] **Generated Client Wrapper:** A `Client` class and associated React hooks (`useQuery`, `useSubscription`) that combine the Generator and Apollo Client for simplified data fetching and mutations (`lib/client.tsx`).
20
29
 
21
30
  **Planned / Future Ideas:**
22
31
 
23
- * [ ] **Convenience Hooks:** Create easy-to-use React hooks (`useQuery`, `useSubscription`, potentially a `useCRUD` hook/class) that integrate the `Generator` with Apollo Client for streamlined data fetching in components.
32
+ * [ ] **Advanced Convenience Hooks:** Potentially create a more advanced `useCRUD` hook/class built on top of the existing `Client` or hooks for even more streamlined common CRUD operations in components.
24
33
  * [ ] **Multi-Platform Builds:** Native builders for Android, iOS, MacOS, Windows, Linux, Oculus (e.g., using Tauri, Capacitor, or Electron).
25
34
  * [ ] **Unique Environment Builders:** Specific builds for Chrome Extensions, Firefox Extensions, and VSCode Extensions (including custom UI elements).
26
35
  * [ ] Additional Authentication Providers (OAuth: Google, GitHub, etc.).
27
36
  * [ ] Role-based access control examples.
28
37
  * [ ] Advanced caching strategies.
29
- * [ ] Comprehensive end-to-end testing setup.
38
+ * [ ] Comprehensive end-to-end testing setup for UI and hooks.
30
39
 
31
40
  ## 🚀 Core Concepts
32
41
 
@@ -62,7 +71,7 @@ Interaction with the Hasura GraphQL Engine is handled in two primary ways:
62
71
 
63
72
  ### 4. Dynamic Query Generation (`GENERATOR.md`)
64
73
 
65
- * The core `Generator` function in `lib/generator.ts` allows you to build complex GraphQL operations dynamically based on a simple options object and your `schema.json`.
74
+ * The core `Generator` function in `lib/generator.ts` allows you to build complex GraphQL operations dynamically based on a simple options object and your `public/hasura-schema.json`.
66
75
  * This avoids writing lengthy GraphQL query strings manually.
67
76
  * See [`GENERATOR.md`](GENERATOR.md) for full usage details and examples.
68
77
  * *Convenience hooks (like `useQuery`, `useSubscription`, `useCRUD`) are planned to further simplify using the generator within React components.*
@@ -84,10 +93,10 @@ Interaction with the Hasura GraphQL Engine is handled in two primary ways:
84
93
  │ ├── debug.ts # Debug utility
85
94
  │ └── ...
86
95
  ├── public/ # Static assets
96
+ │ ├── hasura-schema.json # Hasura GraphQL schema (for Generator)
87
97
  ├── styles/ # Global styles
88
- ├── .env.local # Environment variables (Gitignored)
98
+ ├── .env # Environment variables (Gitignored)
89
99
  ├── GENERATOR.md # Query Generator Documentation
90
- ├── schema.json # Hasura GraphQL schema (for Generator)
91
100
  ├── next.config.js # Next.js configuration
92
101
  ├── package.json # Project dependencies and scripts
93
102
  └── tsconfig.json # TypeScript configuration
@@ -95,23 +104,47 @@ Interaction with the Hasura GraphQL Engine is handled in two primary ways:
95
104
 
96
105
  ## 🛠️ Getting Started
97
106
 
107
+ ### As package
108
+
109
+ 1. **Install the package:**
110
+ ```bash
111
+ npm install nextjs-hasura-auth
112
+ ```
113
+
114
+ 2. **Import the package:**
115
+ ```ts
116
+ import { ... } from 'nextjs-hasura-auth';
117
+ ```
118
+
119
+
120
+ ### As boilerplate
121
+
98
122
  1. **Clone the repository:**
99
123
  ```bash
100
124
  git clone <repository-url>
101
125
  cd <repository-directory>
126
+ git remote add <your-project-name> <your-project-url>
127
+ git push <your-project-name> main
128
+ ```
129
+
130
+ Sync updates from the original repository:
131
+ ```bash
132
+ git fetch origin
133
+ git checkout main
134
+ git merge origin/main
135
+ # solve conflicts
136
+ git push origin main
102
137
  ```
103
138
 
104
139
  2. **Install dependencies:**
105
140
  ```bash
106
- npm install
107
- # or
108
- yarn install
109
- # or
110
- pnpm install
141
+ npm ci
111
142
  ```
112
143
 
113
144
  3. **Set up Environment Variables:**
114
- Create a `.env.local` file in the root directory and add the following variables:
145
+ Create a `.env` file in the root directory and add the following variables:
146
+
147
+ > Create Hasura instance in [Hasura Cloud](https://hasura.io/cloud) or [Hasura Self-Hosted](https://hasura.io/docs/latest/graphql/core/deployment/hasura-cli/hasura-cli-install/)
115
148
 
116
149
  ```env
117
150
  # Hasura Configuration
@@ -130,7 +163,7 @@ Interaction with the Hasura GraphQL Engine is handled in two primary ways:
130
163
  * Ensure `NEXTAUTH_URL` points to your application's base URL.
131
164
 
132
165
  4. **Update Hasura Schema:**
133
- Make sure the `schema.json` file in the root is up-to-date with your Hasura instance's schema. You might need to fetch this from Hasura if you've made changes.
166
+ Make sure the `public/hasura-schema.json` file in the root is up-to-date with your Hasura instance's schema. You might need to fetch this from Hasura if you've made changes.
134
167
 
135
168
  5. **Run the development server:**
136
169
  ```bash
@@ -143,6 +176,36 @@ Interaction with the Hasura GraphQL Engine is handled in two primary ways:
143
176
 
144
177
  6. Open [http://localhost:3000](http://localhost:3000) (or your `NEXTAUTH_URL`) in your browser.
145
178
 
179
+ 7. Configure Google OAuth
180
+ - Go to [Google Cloud Console](https://console.cloud.google.com/)
181
+ - Create a new project
182
+ - Enable Google OAuth API
183
+ - Create credentials
184
+ - Add `http://localhost:3000/api/auth/callback/google` as a redirect URI
185
+ - Copy the client ID and client secret
186
+ - Add them to the `.env` file or vercel environment variables
187
+ ```env
188
+ GOOGLE_CLIENT_ID="<your-google-client-id>"
189
+ GOOGLE_CLIENT_SECRET="<your-google-client-secret>"
190
+ ```
191
+
192
+ 8. Configure Yandex OAuth
193
+ - Go to [Yandex Cloud Console](https://oauth.yandex.com/client/new)
194
+ - Create a new application
195
+ - Add `http://localhost:3000/api/auth/callback/yandex` as a redirect URI
196
+ - Copy the client ID and client secret
197
+ - Add them to the `.env` file or vercel environment variables
198
+ ```env
199
+ YANDEX_CLIENT_ID="<your-yandex-client-id>"
200
+ YANDEX_CLIENT_SECRET="<your-yandex-client-secret>"
201
+ ```
202
+
203
+ 9. Configure Vercel
204
+ - Go to [Vercel](https://vercel.com/)
205
+ - Create a new project
206
+ - Add vercel environment variables from `.env` file
207
+ - Deploy the project
208
+
146
209
  ## Environment Variables Summary
147
210
 
148
211
  * `NEXT_PUBLIC_HASURA_GRAPHQL_ENDPOINT`: Public URL for Hasura GraphQL HTTP endpoint.
@@ -150,5 +213,3 @@ Interaction with the Hasura GraphQL Engine is handled in two primary ways:
150
213
  * `HASURA_ADMIN_SECRET`: Your Hasura admin secret (kept server-side).
151
214
  * `NEXTAUTH_URL`: The canonical URL of your Next.js application.
152
215
  * `NEXTAUTH_SECRET`: A secret key used by NextAuth.js to sign JWTs, etc.
153
-
154
- Let me know what you think!
@@ -1,15 +1,15 @@
1
1
  import { ApolloClient } from '@apollo/client';
2
- /**
3
- * Get JWT secret from environment variables
4
- * @returns {Uint8Array} Secret key for JWT signing
5
- */
6
- export declare const getJwtSecret: () => Uint8Array;
7
- interface ApolloOptions {
2
+ export interface ApolloOptions {
8
3
  url?: string;
9
4
  ws?: boolean;
10
5
  token?: string;
11
6
  secret?: string;
12
7
  }
8
+ export interface ApolloClientWithProvider extends ApolloClient<any> {
9
+ Provider: React.ComponentType<{
10
+ children: React.ReactNode;
11
+ }>;
12
+ }
13
13
  /**
14
14
  * Create Apollo Client
15
15
  *
@@ -19,7 +19,7 @@ interface ApolloOptions {
19
19
  * @param {string} options.secret - Admin secret for Hasura
20
20
  * @returns {ApolloClient} Apollo Client
21
21
  */
22
- export declare function createClient(options?: ApolloOptions): ApolloClient<import("@apollo/client").NormalizedCacheObject>;
22
+ export declare function createApolloClient(options?: ApolloOptions): ApolloClientWithProvider;
23
23
  /**
24
24
  * Get or create Apollo client instance
25
25
  * @param options Client options
@@ -30,14 +30,14 @@ export declare function getClient(options?: {}): ApolloClient<any>;
30
30
  * React hook to get Apollo client instance
31
31
  * @returns Apollo client instance
32
32
  */
33
- export declare function useClient(options: ApolloOptions): ApolloClient<import("@apollo/client").NormalizedCacheObject>;
33
+ export declare function useCreateApolloClient(options: ApolloOptions): ApolloClientWithProvider;
34
34
  /**
35
35
  * Check connection to Hasura GraphQL endpoint
36
36
  * @returns {Promise<boolean>} True if connection is successful
37
37
  */
38
38
  export declare function checkConnection(client?: ApolloClient<any>): Promise<boolean>;
39
39
  declare const _default: {
40
- createClient: typeof createClient;
40
+ createApolloClient: typeof createApolloClient;
41
41
  getClient: typeof getClient;
42
42
  getJwtSecret: () => Uint8Array;
43
43
  checkConnection: typeof checkConnection;
@@ -12,10 +12,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getJwtSecret = void 0;
16
- exports.createClient = createClient;
15
+ exports.createApolloClient = createApolloClient;
17
16
  exports.getClient = getClient;
18
- exports.useClient = useClient;
17
+ exports.useCreateApolloClient = useCreateApolloClient;
19
18
  exports.checkConnection = checkConnection;
20
19
  const client_1 = require("@apollo/client");
21
20
  const context_1 = require("@apollo/client/link/context");
@@ -25,39 +24,11 @@ const graphql_ws_1 = require("graphql-ws");
25
24
  const cross_fetch_1 = __importDefault(require("cross-fetch"));
26
25
  const debug_1 = __importDefault(require("./debug"));
27
26
  const react_1 = require("react");
27
+ const jwt_1 = require("./jwt");
28
28
  // Create a debug logger for this module
29
29
  const debug = (0, debug_1.default)('apollo');
30
30
  // Determine if running on client
31
31
  const isClient = typeof window !== 'undefined';
32
- /**
33
- * Get JWT secret from environment variables
34
- * @returns {Uint8Array} Secret key for JWT signing
35
- */
36
- const getJwtSecret = () => {
37
- try {
38
- const jwtSecret = process.env.HASURA_JWT_SECRET || '{"type":"HS256","key":"your-secret-key"}';
39
- // Parse JWT configuration (may be in JSON format)
40
- let secretKey;
41
- try {
42
- const jwtConfig = typeof jwtSecret === 'string' ? JSON.parse(jwtSecret) : jwtSecret;
43
- secretKey = jwtConfig.key;
44
- if (!secretKey) {
45
- throw new Error('JWT key not found in configuration');
46
- }
47
- }
48
- catch (e) {
49
- // If failed to parse as JSON, use as string
50
- secretKey = jwtSecret;
51
- }
52
- // Convert key to Uint8Array (required for jose)
53
- return new TextEncoder().encode(secretKey);
54
- }
55
- catch (error) {
56
- debug('apollo', '❌ Error getting JWT secret:', error);
57
- throw error;
58
- }
59
- };
60
- exports.getJwtSecret = getJwtSecret;
61
32
  /**
62
33
  * Create Apollo Client
63
34
  *
@@ -67,7 +38,7 @@ exports.getJwtSecret = getJwtSecret;
67
38
  * @param {string} options.secret - Admin secret for Hasura
68
39
  * @returns {ApolloClient} Apollo Client
69
40
  */
70
- function createClient(options = {}) {
41
+ function createApolloClient(options = {}) {
71
42
  // Default values
72
43
  const { url = process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL, ws = false, token = undefined, secret = process.env.HASURA_ADMIN_SECRET } = options;
73
44
  if (!url) {
@@ -140,7 +111,7 @@ function createClient(options = {}) {
140
111
  }, wsLink, httpLink);
141
112
  }
142
113
  // Create Apollo Client
143
- return new client_1.ApolloClient({
114
+ const apolloClient = new client_1.ApolloClient({
144
115
  link: splitLink,
145
116
  cache: new client_1.InMemoryCache(),
146
117
  defaultOptions: {
@@ -157,6 +128,10 @@ function createClient(options = {}) {
157
128
  },
158
129
  }
159
130
  });
131
+ apolloClient.Provider = function Provider({ children }) {
132
+ return <client_1.ApolloProvider client={apolloClient}>{children}</client_1.ApolloProvider>;
133
+ };
134
+ return apolloClient;
160
135
  }
161
136
  // Default client instance
162
137
  let clientInstance = null;
@@ -167,7 +142,7 @@ let clientInstance = null;
167
142
  */
168
143
  function getClient(options = {}) {
169
144
  if (!clientInstance) {
170
- clientInstance = createClient(options);
145
+ clientInstance = createApolloClient(options);
171
146
  }
172
147
  return clientInstance;
173
148
  }
@@ -175,8 +150,8 @@ function getClient(options = {}) {
175
150
  * React hook to get Apollo client instance
176
151
  * @returns Apollo client instance
177
152
  */
178
- function useClient(options) {
179
- return (0, react_1.useMemo)(() => createClient(options), [options]);
153
+ function useCreateApolloClient(options) {
154
+ return (0, react_1.useMemo)(() => createApolloClient(options), [options]);
180
155
  }
181
156
  const CHECK_CONNECTION = (0, client_1.gql) `
182
157
  query CheckConnection {
@@ -199,8 +174,8 @@ function checkConnection() {
199
174
  });
200
175
  }
201
176
  exports.default = {
202
- createClient,
177
+ createApolloClient,
203
178
  getClient,
204
- getJwtSecret: exports.getJwtSecret,
179
+ getJwtSecret: jwt_1.getJwtSecret,
205
180
  checkConnection
206
181
  };
@@ -0,0 +1,16 @@
1
+ import { NextRequest } from "next/server";
2
+ import { IncomingMessage } from "http";
3
+ import { JWT } from 'next-auth/jwt';
4
+ import WebSocket from "ws";
5
+ export declare function getTokenFromRequest(request: NextRequest): Promise<JWT | null>;
6
+ export declare function WsClientsManager(route?: string): {
7
+ Client: (client: WebSocket) => string;
8
+ getToken(request: IncomingMessage, clientId: string): Promise<string | JWT | null>;
9
+ parseUser(request: IncomingMessage, clientId: string): Promise<any>;
10
+ delete(clientId: string): void;
11
+ getClient(clientId: string): {
12
+ ws: WebSocket;
13
+ userId?: string;
14
+ user?: any;
15
+ } | undefined;
16
+ };