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 +106 -80
- package/GENERATOR.md +4 -5
- package/README.md +75 -14
- package/dist/lib/apollo.d.ts +9 -9
- package/dist/lib/{apollo.js → apollo.jsx} +14 -39
- package/dist/lib/auth.d.ts +16 -0
- package/dist/lib/auth.jsx +159 -0
- package/dist/lib/client.d.ts +82 -0
- package/dist/lib/client.jsx +237 -0
- package/dist/lib/generator.d.ts +2 -7
- package/dist/lib/generator.js +394 -392
- package/dist/lib/hasura-schema.d.ts +1 -0
- package/dist/lib/hasura-schema.js +72 -0
- package/dist/lib/hasura.d.ts +16 -0
- package/dist/lib/hasura.js +102 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.js +4 -0
- package/dist/package.json +22 -5
- package/dist/public/hasura-schema.json +7995 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +18 -4
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
|
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
|
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
|
13
|
-
* **WebSocket Connections (Subscriptions):**
|
14
|
-
* **
|
15
|
-
* **
|
16
|
-
* **
|
17
|
-
* **
|
18
|
-
* **Client-Side
|
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. **
|
25
|
-
|
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
|
29
|
-
'use client' //
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
80
|
+
{/* SessionProvider is required for useSession hook */}
|
46
81
|
<SessionProvider>
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
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
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
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. **
|
105
|
-
|
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
|
109
|
-
import {
|
110
|
-
|
111
|
-
// import
|
112
|
-
|
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
|
-
|
115
|
-
const
|
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,
|
155
|
+
query: GET_USER,
|
123
156
|
variables: { userId },
|
124
|
-
//
|
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
|
-
|
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
|
-
|
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
|
-
*(
|
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
|
-
|
17
|
+
import schema from './public/hasura-schema.json';
|
18
18
|
|
19
19
|
// Initialize the generator (Schema needs to be passed)
|
20
|
-
|
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.md) [](APOLLO.md)
|
6
|
+
[](AUTH.md) [](HASURA.md)
|
7
|
+
[](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:**
|
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
|
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
|
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
|
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!
|
package/dist/lib/apollo.d.ts
CHANGED
@@ -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
|
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
|
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
|
-
|
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.
|
16
|
-
exports.createClient = createClient;
|
15
|
+
exports.createApolloClient = createApolloClient;
|
17
16
|
exports.getClient = getClient;
|
18
|
-
exports.
|
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
|
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
|
-
|
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 =
|
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
|
179
|
-
return (0, react_1.useMemo)(() =>
|
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
|
-
|
177
|
+
createApolloClient,
|
203
178
|
getClient,
|
204
|
-
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
|
+
};
|