Skip to content

Commit af4d50a

Browse files
webberwangclaude
andcommitted
refactor: consolidate fetch infrastructure and enable request batching
- Extract shared batching logic from server to client module - Rename server files to follow .server.ts convention - Enable Apollo Server batch operation support - Configure batch fetcher with 20ms window and max 10 operations - Eliminate code duplication between client and server fetch implementations Co-Authored-By: Claude <noreply@anthropic.com>
1 parent abf28c8 commit af4d50a

File tree

14 files changed

+435
-93
lines changed

14 files changed

+435
-93
lines changed

libs/backend/infra/adapter/graphql/src/graphql-client.provider.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

libs/backend/infra/adapter/graphql/src/graphql.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export class GraphqlModule {
7979
) => {
8080
return {
8181
// bodyParserConfig: false,
82+
allowBatchedHttpRequests: true,
8283
context: async ({ req, res }: GqlContext) => {
8384
// starting from neo4j/graphql v5 - token is required in order to allow
8485
// @authentication/@authorization in neo4j schema files, see migration guide:
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# GraphQL Request Batching
2+
3+
## Overview
4+
5+
GraphQL request batching is now built directly into `serverFetchWithAuth`. Multiple GraphQL requests that occur within a time window are automatically combined into a single HTTP request, improving performance and reducing network overhead. This happens transparently - no code changes required!
6+
7+
## How It Works
8+
9+
```typescript
10+
import { gqlServerRequest } from '@codelab/shared-infra-fetch-server'
11+
12+
// These requests are automatically batched
13+
const [users, posts, comments] = await Promise.all([
14+
gqlServerRequest(GetUsersDocument, { limit: 10 }),
15+
gqlServerRequest(GetPostsDocument, { userId: 1 }),
16+
gqlServerRequest(GetCommentsDocument, { postId: 1 })
17+
])
18+
// These three requests are combined into a single HTTP call!
19+
```
20+
21+
The batching happens inside `serverFetchWithAuth`:
22+
1. It detects GraphQL requests by checking for a `query` field in the request body
23+
2. GraphQL requests are queued and batched within a 20ms window
24+
3. Up to 10 operations are combined into a single HTTP request
25+
4. Non-GraphQL requests bypass batching and go directly through
26+
27+
## Configuration
28+
29+
Currently, the batching configuration is fixed at:
30+
- **Batch interval**: 20ms (time window to collect requests)
31+
- **Max batch size**: 10 operations per batch
32+
33+
These values are optimized for most use cases and provide a good balance between latency and efficiency.
34+
35+
## Benefits
36+
37+
1. **Reduced Network Overhead**: Fewer HTTP connections and handshakes
38+
2. **Better Performance**: Especially for pages loading multiple resources
39+
3. **Efficient Auth Token Usage**: One token retrieval per batch instead of per request
40+
4. **ECONNRESET Mitigation**: Fewer concurrent connections reduce connection reset errors
41+
5. **Transparent**: No code changes required for existing `gqlServerRequest` calls
42+
43+
## Implementation Details
44+
45+
- GraphQL requests are automatically detected and batched
46+
- Non-GraphQL requests (REST APIs, file uploads, etc.) are not affected
47+
- Authentication is handled once per batch for all operations
48+
- Each request in a batch maintains its own promise that resolves/rejects independently
49+
- The batch fetcher uses a singleton pattern for efficiency
50+
51+
## Requirements
52+
53+
- The GraphQL server must support batched queries (Apollo Server supports this by default)
54+
- Authentication is handled once per batch for all operations
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use server'
2+
3+
import {
4+
batchFetch as clientBatchFetch,
5+
configureBatchFetcher,
6+
resetBatchFetcher as clientResetBatchFetcher,
7+
} from '@codelab/shared-infra-fetch'
8+
import { logger } from '@codelab/shared-infra-logging'
9+
10+
// Configure the batch fetcher with server logger on first import
11+
configureBatchFetcher({ logger })
12+
13+
/**
14+
* Server-side batch fetch wrapper
15+
*/
16+
export const batchFetch = async (
17+
url: string,
18+
init: RequestInit = {},
19+
): Promise<Response> => {
20+
return clientBatchFetch(url, init)
21+
}
22+
23+
/**
24+
* Reset the batch fetcher instance (useful for testing)
25+
*/
26+
export const resetBatchFetcher = async () => {
27+
clientResetBatchFetcher()
28+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use server'
2+
3+
import { fetchWithAuth } from '@codelab/shared-infra-fetch'
4+
5+
/**
6+
* Server-side fetch with authentication wrapper
7+
*/
8+
export const serverFetchWithAuth = async (
9+
endpoint: string,
10+
init: RequestInit,
11+
): Promise<Response> => {
12+
return fetchWithAuth(endpoint, init)
13+
}

libs/shared/infra/fetch-server/src/gql-server-request.ts renamed to libs/shared/infra/fetch-server/src/gql-request.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { logger } from '@codelab/shared-infra-logging'
1111
import { TRACING_HEADERS } from '@codelab/shared-infra-tracing'
1212
import { revalidateTag } from 'next/cache'
1313

14-
import { serverFetchWithAuth } from './server-fetch-with-auth'
14+
import { serverFetchWithAuth } from './fetch-with-auth.server'
1515

1616
/**
1717
* Server-side GraphQL request function that handles authentication and logging.
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
export { gqlServerRequest } from './gql-server-request'
2-
export * from './server-fetch-with-auth'
1+
export { resetBatchFetcher } from './batch-fetch.server'
2+
export * from './fetch-with-auth.server'
3+
export { gqlServerRequest } from './gql-request.server'

libs/shared/infra/fetch-server/src/server-fetch-with-auth.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)