cfw-graphql-bootstrap 1.0.0

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/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # cfw-graphql-bootstrap
2
+
3
+ Enterprise-grade GraphQL server framework for Cloudflare Workers with built-in security, caching, and observability.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Optimized for Cloudflare Workers** - Built specifically for edge runtime with minimal bundle size
8
+ - 🔒 **Enterprise Security** - HMAC signature verification, CORS, rate limiting, and security headers
9
+ - 💾 **Multi-tier Caching** - Support for Cloudflare KV and Redis with cache-aside pattern
10
+ - 📊 **Observability** - Distributed tracing, structured logging with Pino, and health checks
11
+ - 🛡️ **Type Safety** - Full TypeScript support with proper type exports
12
+ - ⚡ **High Performance** - Lightweight environment parsing, efficient middleware chain
13
+ - 🔧 **Developer Experience** - Easy setup, extensible context, and modular architecture
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install cfw-graphql-bootstrap
19
+ # or
20
+ bun add cfw-graphql-bootstrap
21
+ ```
22
+
23
+ ### Peer Dependencies
24
+
25
+ This package requires the following peer dependencies:
26
+
27
+ ```json
28
+ {
29
+ "@apollo/server": "^5.0.0",
30
+ "@cloudflare/workers-types": "^4.0.0",
31
+ "hono": "^4.0.0",
32
+ "typescript": "^5.0.0"
33
+ }
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```typescript
39
+ import {
40
+ createGraphQLServer,
41
+ createSchemaModule,
42
+ gql,
43
+ } from 'cfw-graphql-bootstrap';
44
+
45
+ // Define your schema
46
+ const schema = createSchemaModule({
47
+ typeDefs: gql`
48
+ type Query {
49
+ hello(name: String!): String
50
+ }
51
+ `,
52
+ resolvers: {
53
+ Query: {
54
+ hello: (_, {name}) => `Hello ${name}!`,
55
+ },
56
+ },
57
+ });
58
+
59
+ // Create the server
60
+ const server = createGraphQLServer({
61
+ name: 'my-graphql-api',
62
+ version: '1.0.0',
63
+ config: {
64
+ apollo: {
65
+ schema: schema.schema,
66
+ },
67
+ datasources: (cache, context) => ({}),
68
+ },
69
+ });
70
+
71
+ // Export for Cloudflare Workers
72
+ export default server.configure();
73
+ ```
74
+
75
+ ## Advanced Usage
76
+
77
+ ### Custom Context
78
+
79
+ Extend the base context with your own properties:
80
+
81
+ ```typescript
82
+ import {createGraphQLServer, type BaseContext} from 'cfw-graphql-bootstrap';
83
+
84
+ interface MyContext extends BaseContext {
85
+ dataSources: {
86
+ userAPI: UserAPI;
87
+ };
88
+ }
89
+
90
+ const server = createGraphQLServer<any, MyContext>({
91
+ name: 'my-api',
92
+ version: '1.0.0',
93
+ config: {
94
+ apollo: {schema},
95
+ datasources: (cache, context) => ({
96
+ userAPI: new UserAPI(context),
97
+ }),
98
+ extendContext: async (baseContext, honoContext) => ({
99
+ ...baseContext,
100
+ // Add custom context properties
101
+ }),
102
+ },
103
+ });
104
+ ```
105
+
106
+ ### Caching
107
+
108
+ Enable KV caching by binding a KV namespace:
109
+
110
+ ```toml
111
+ # wrangler.toml
112
+ [[kv_namespaces]]
113
+ binding = "CACHE_KV"
114
+ id = "your-kv-namespace-id"
115
+ ```
116
+
117
+ Use the cache decorator in resolvers:
118
+
119
+ ```typescript
120
+ import {cached} from 'cfw-graphql-bootstrap';
121
+
122
+ class UserResolver {
123
+ @cached({ttl: 300}) // Cache for 5 minutes
124
+ async getUser(_, {id}, context) {
125
+ return fetchUserFromDB(id);
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Environment Configuration
131
+
132
+ The framework automatically validates and parses environment variables:
133
+
134
+ ```typescript
135
+ // Available in context.env
136
+ interface EnvConfig {
137
+ NODE_ENV: 'development' | 'test' | 'production';
138
+ PORT: number;
139
+ GATEWAY_SECRET?: string;
140
+ ALLOWED_ORIGINS: string[];
141
+ LOG_LEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
142
+ RATE_LIMIT_WINDOW_MS: number;
143
+ RATE_LIMIT_MAX: number;
144
+ // ... and more
145
+ }
146
+ ```
147
+
148
+ ### Middleware
149
+
150
+ Add custom middleware or configure built-in ones:
151
+
152
+ ```typescript
153
+ import {
154
+ createRateLimitMiddleware,
155
+ createValidationMiddleware,
156
+ createTracingMiddleware,
157
+ configureHealthChecks,
158
+ } from 'cfw-graphql-bootstrap';
159
+
160
+ // All middleware is automatically configured
161
+ // Customize if needed in your server setup
162
+ ```
163
+
164
+ ### Logging
165
+
166
+ Structured logging with Pino, optimized for Cloudflare Workers:
167
+
168
+ ```typescript
169
+ import {logger} from 'cfw-graphql-bootstrap';
170
+
171
+ logger.info({userId: '123'}, 'User action');
172
+ logger.error({err: error}, 'Operation failed');
173
+ ```
174
+
175
+ ### Security
176
+
177
+ Built-in security features:
178
+
179
+ - HMAC signature verification for gateway communication
180
+ - Rate limiting with Cloudflare KV
181
+ - CORS configuration
182
+ - Security headers (CSP, HSTS, etc.)
183
+ - Request validation
184
+
185
+ ## API Reference
186
+
187
+ ### Core Exports
188
+
189
+ - `createGraphQLServer(options)` - Create a new GraphQL server
190
+ - `ServerBuilder` - Low-level server builder class
191
+ - `createContextFunction(extendFn?)` - Create context factory
192
+
193
+ ### Context & Types
194
+
195
+ - `BaseContext` - Base context interface
196
+ - `ContextUser` - User context type
197
+ - `ContextExtendFunction` - Context extension function type
198
+
199
+ ### Environment
200
+
201
+ - `envLoader` - Environment configuration loader
202
+ - `EnvConfig` - Environment configuration type
203
+
204
+ ### Cache
205
+
206
+ - `createCache(env)` - Create cache instance
207
+ - `KVCache` - Cloudflare KV cache implementation
208
+ - `RedisCache` - Redis cache implementation
209
+ - `@cached(options)` - Cache decorator
210
+
211
+ ### Middleware
212
+
213
+ - `createRateLimitMiddleware(options)`
214
+ - `createValidationMiddleware(options)`
215
+ - `createTracingMiddleware()`
216
+ - `configureHealthChecks(app, options)`
217
+
218
+ ### Utilities
219
+
220
+ - `createSchemaModule(config)` - Create GraphQL schema
221
+ - `gql` - GraphQL template literal tag
222
+ - `AppError` - Application error class
223
+ - `logger` - Pino logger instance
224
+
225
+ ## Deployment
226
+
227
+ ### Wrangler Configuration
228
+
229
+ ```toml
230
+ # wrangler.toml
231
+ name = "my-graphql-api"
232
+ main = "dist/index.js"
233
+ compatibility_date = "2024-01-01"
234
+
235
+ [env.production]
236
+ vars = {
237
+ NODE_ENV = "production",
238
+ ALLOWED_ORIGINS = "https://myapp.com",
239
+ RATE_LIMIT_MAX = "100"
240
+ }
241
+
242
+ [[kv_namespaces]]
243
+ binding = "CACHE_KV"
244
+ id = "your-kv-namespace-id"
245
+
246
+ [[kv_namespaces]]
247
+ binding = "RATE_LIMIT_KV"
248
+ id = "your-rate-limit-kv-id"
249
+ ```
250
+
251
+ ### Deploy
252
+
253
+ ```bash
254
+ # Development
255
+ wrangler dev
256
+
257
+ # Production
258
+ wrangler deploy --env production
259
+ ```
260
+
261
+ ## Performance
262
+
263
+ - **Bundle Size**: ~30KB (ESM, before compression)
264
+ - **Cold Start**: < 10ms typical
265
+ - **Request Overhead**: < 2ms for middleware chain
266
+ - **Memory Usage**: Optimized for Workers' 128MB limit
267
+
268
+ ## Contributing
269
+
270
+ Contributions are welcome! Please read our contributing guidelines before submitting PRs.
271
+
272
+ ## License
273
+
274
+ MIT
275
+
276
+ ## Support
277
+
278
+ For issues and feature requests, please use the [GitHub issue tracker](https://github.com/yourusername/cfw-graphql-bootstrap/issues).
@@ -0,0 +1,278 @@
1
+ import { BaseContext as BaseContext$1, ApolloServerOptions, ApolloServerPlugin } from '@apollo/server';
2
+ import { KVNamespace, ExecutionContext } from '@cloudflare/workers-types';
3
+ import { CloudflareContextFunctionArgument } from '@as-integrations/cloudflare-workers';
4
+ import { GraphQLSchema, GraphQLError, DocumentNode } from 'graphql';
5
+ import { Hono, MiddlewareHandler } from 'hono';
6
+ import { ApolloServerErrorCode } from '@apollo/server/errors';
7
+ export { gql } from 'graphql-tag';
8
+
9
+ declare const logger: any;
10
+ declare global {
11
+ var logshipper: {
12
+ log: (data: any) => void;
13
+ };
14
+ var requestContext: {
15
+ traceId?: string;
16
+ spanId?: string;
17
+ userId?: string;
18
+ };
19
+ }
20
+
21
+ interface ContextUser {
22
+ id: string;
23
+ email?: string;
24
+ roles?: string[];
25
+ permissions?: string[];
26
+ }
27
+
28
+ /**
29
+ * KV Cache implementation for Cloudflare Workers
30
+ * Provides caching capabilities for GraphQL resolvers using Cloudflare KV
31
+ */
32
+
33
+ interface CacheOptions {
34
+ ttl?: number;
35
+ cacheKey?: string;
36
+ }
37
+ declare class KVCache {
38
+ private kv;
39
+ constructor(kv: KVNamespace);
40
+ /**
41
+ * Get a value from cache
42
+ */
43
+ get<T = any>(key: string): Promise<T | null>;
44
+ /**
45
+ * Set a value in cache
46
+ */
47
+ set(key: string, value: any, options?: CacheOptions): Promise<void>;
48
+ /**
49
+ * Delete a value from cache
50
+ */
51
+ delete(key: string): Promise<void>;
52
+ /**
53
+ * Check if a key exists in cache
54
+ */
55
+ has(key: string): Promise<boolean>;
56
+ /**
57
+ * Clear multiple keys matching a prefix
58
+ */
59
+ clearPrefix(prefix: string): Promise<void>;
60
+ /**
61
+ * Get or set a value with a factory function
62
+ * Useful for cache-aside pattern
63
+ */
64
+ getOrSet<T>(key: string, factory: () => Promise<T>, options?: CacheOptions): Promise<T>;
65
+ }
66
+
67
+ /**
68
+ * Redis Cache implementation for Cloudflare Workers
69
+ * Alternative to KV for more advanced caching needs
70
+ */
71
+
72
+ declare class RedisCache {
73
+ private redis;
74
+ constructor(config?: {
75
+ url?: string;
76
+ token?: string;
77
+ });
78
+ private initUpstash;
79
+ private createMockRedis;
80
+ get<T = any>(key: string): Promise<T | null>;
81
+ set(key: string, value: any, options?: CacheOptions): Promise<void>;
82
+ delete(key: string): Promise<void>;
83
+ has(key: string): Promise<boolean>;
84
+ clearPrefix(prefix: string): Promise<void>;
85
+ getOrSet<T>(key: string, factory: () => Promise<T>, options?: CacheOptions): Promise<T>;
86
+ increment(key: string, by?: number): Promise<number>;
87
+ expire(key: string, seconds: number): Promise<boolean>;
88
+ ttl(key: string): Promise<number>;
89
+ }
90
+
91
+ /**
92
+ * Cache module for GraphQL resolvers
93
+ * Provides unified caching interface with multiple backends
94
+ */
95
+
96
+ /**
97
+ * Cache factory - creates appropriate cache based on environment
98
+ */
99
+ declare function createCache(env: any): KVCache | RedisCache | null;
100
+ /**
101
+ * Decorator for caching resolver results
102
+ */
103
+ declare function cached(options?: CacheOptions & {
104
+ keyGenerator?: (...args: any[]) => string;
105
+ }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
106
+ /**
107
+ * GraphQL DataLoader integration for batching and caching
108
+ */
109
+ declare class CachedDataLoader<K, V> {
110
+ private options;
111
+ private loader;
112
+ private cache;
113
+ constructor(batchFn: (keys: K[]) => Promise<V[]>, cache: KVCache | RedisCache, options?: CacheOptions);
114
+ load(key: K): Promise<V>;
115
+ loadMany(keys: K[]): Promise<V[]>;
116
+ clear(key: K): Promise<void>;
117
+ clearAll(): Promise<void>;
118
+ }
119
+
120
+ /**
121
+ * Environment configuration type
122
+ */
123
+ interface EnvConfig {
124
+ NODE_ENV: 'development' | 'test' | 'production';
125
+ PORT: number;
126
+ GATEWAY_SECRET?: string;
127
+ ALLOWED_ORIGINS: string[];
128
+ LOG_LEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
129
+ RATE_LIMIT_WINDOW_MS: number;
130
+ RATE_LIMIT_MAX: number;
131
+ ENABLE_METRICS: boolean;
132
+ ENABLE_TRACING: boolean;
133
+ MAX_QUERY_DEPTH: number;
134
+ MAX_QUERY_COMPLEXITY: number;
135
+ INTROSPECTION_ENABLED: boolean;
136
+ REDIS_URL?: string;
137
+ REDIS_TOKEN?: string;
138
+ LOG_SERVICE_URL?: string;
139
+ LOG_SERVICE_TOKEN?: string;
140
+ }
141
+ /**
142
+ * Environment loader for Cloudflare Workers
143
+ */
144
+ declare class EnvironmentLoader {
145
+ private config;
146
+ /**
147
+ * Load environment from Cloudflare Workers env object
148
+ */
149
+ load(env: Record<string, any>): EnvConfig;
150
+ /**
151
+ * Get current configuration
152
+ */
153
+ getConfig(): EnvConfig;
154
+ /**
155
+ * Helper getters for common checks
156
+ */
157
+ get isDev(): boolean;
158
+ get isTest(): boolean;
159
+ get isProd(): boolean;
160
+ }
161
+ declare const envLoader: EnvironmentLoader;
162
+
163
+ interface JaegerContext {
164
+ rootSpan?: {
165
+ getBaggageItem: (key: string) => string | undefined;
166
+ };
167
+ }
168
+ interface BaseContext extends JaegerContext, BaseContext$1 {
169
+ user?: ContextUser;
170
+ logger: typeof logger;
171
+ executionCtx: ExecutionContext;
172
+ cache?: KVCache;
173
+ env: EnvConfig;
174
+ /** @deprecated should not be used directly */
175
+ authorization: string;
176
+ clientId: string;
177
+ }
178
+ type ContextExtendFunction<T extends BaseContext> = (ctx: BaseContext, honoContext: CloudflareContextFunctionArgument<unknown>) => T | Promise<T>;
179
+ declare function createContextFunction<T extends BaseContext>(extendContext?: ContextExtendFunction<T>): (honoContext: CloudflareContextFunctionArgument<unknown>) => Promise<BaseContext>;
180
+
181
+ type Runner = {
182
+ fetch: typeof Hono.prototype.fetch;
183
+ port?: number;
184
+ };
185
+ type ServerConfig<TDatasource, TContext extends BaseContext> = {
186
+ path?: string;
187
+ apollo: Partial<ApolloServerOptions<BaseContext>>;
188
+ datasources: (cache: any, context: TContext) => TDatasource;
189
+ extendContext?: ContextExtendFunction<TContext>;
190
+ };
191
+ declare class ServerBuilder<TDatasource, TContext extends BaseContext> {
192
+ readonly config: ServerConfig<TDatasource, TContext>;
193
+ readonly app: Hono;
194
+ readonly schema: GraphQLSchema;
195
+ readonly plugins: ApolloServerPlugin<BaseContext>[];
196
+ constructor(config: ServerConfig<TDatasource, TContext>);
197
+ configure(): Runner;
198
+ assert(): void;
199
+ }
200
+
201
+ type BaseServerOptions = {
202
+ /**
203
+ * The version of the server
204
+ *
205
+ * @type {string}
206
+ */
207
+ version: string;
208
+ /**
209
+ * The name of the server
210
+ *
211
+ * @type {string}
212
+ */
213
+ name: string;
214
+ /**
215
+ * The port to listen on
216
+ *
217
+ * @type {number}
218
+ */
219
+ port?: number;
220
+ /**
221
+ * The hostname to listen on
222
+ *
223
+ * @type {string}
224
+ */
225
+ hostname?: string;
226
+ };
227
+ type ServerOptions<TDatasource, TContext extends BaseContext> = BaseServerOptions & {
228
+ config: ServerConfig<TDatasource, TContext>;
229
+ };
230
+ declare function createGraphQLServer<TDatasource, TContext extends BaseContext>(options: ServerOptions<TDatasource, TContext>): ServerBuilder<TDatasource, TContext>;
231
+
232
+ declare function createApolloLoggingPlugin(): ApolloServerPlugin<BaseContext>;
233
+
234
+ interface RateLimitOptions {
235
+ windowMs?: number;
236
+ max?: number;
237
+ keyGenerator?: (c: any) => string;
238
+ }
239
+ declare function createRateLimitMiddleware(options?: RateLimitOptions): MiddlewareHandler;
240
+
241
+ interface ValidationOptions {
242
+ maxQueryDepth?: number;
243
+ maxQueryComplexity?: number;
244
+ maxAliasCount?: number;
245
+ }
246
+ declare function createValidationMiddleware(options?: ValidationOptions): MiddlewareHandler;
247
+
248
+ declare function createTracingMiddleware(): MiddlewareHandler;
249
+
250
+ interface HealthCheckOptions {
251
+ checks?: {
252
+ [key: string]: () => Promise<boolean>;
253
+ };
254
+ }
255
+ declare function configureHealthChecks(app: Hono, options?: HealthCheckOptions): void;
256
+
257
+ declare enum ErrorCode {
258
+ UNAUTHENTICATED = "UNAUTHENTICATED",
259
+ FORBIDDEN = "FORBIDDEN",
260
+ VALIDATION_FAILED = "VALIDATION_FAILED",
261
+ RATE_LIMITED = "RATE_LIMITED",
262
+ EXTERNAL_SERVICE_ERROR = "EXTERNAL_SERVICE_ERROR",
263
+ NOT_FOUND = "NOT_FOUND",
264
+ CONFLICT = "CONFLICT"
265
+ }
266
+ declare class AppError extends GraphQLError {
267
+ constructor(message: string, code: ErrorCode | ApolloServerErrorCode, extensions?: Record<string, any>);
268
+ }
269
+
270
+ declare const createSchemaModule: (typeDefs: DocumentNode, resolvers?: any) => {
271
+ typeDefs: DocumentNode;
272
+ resolvers: any;
273
+ };
274
+ type SchemaModule = ReturnType<typeof createSchemaModule>;
275
+
276
+ declare const getNonUserInputErrors: (errors: readonly GraphQLError[]) => GraphQLError[];
277
+
278
+ export { AppError, type BaseContext, type BaseServerOptions, type CacheOptions, CachedDataLoader, type ContextExtendFunction, type ContextUser, type EnvConfig, ErrorCode, KVCache, RedisCache, type Runner, type SchemaModule, ServerBuilder, type ServerConfig, type ServerOptions, cached, configureHealthChecks, createApolloLoggingPlugin, createCache, createContextFunction, createGraphQLServer, createRateLimitMiddleware, createSchemaModule, createTracingMiddleware, createValidationMiddleware, envLoader, getNonUserInputErrors, logger };