@jaypie/mcp 0.1.9 → 0.2.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.
Files changed (41) hide show
  1. package/dist/datadog.d.ts +212 -0
  2. package/dist/index.js +1461 -6
  3. package/dist/index.js.map +1 -1
  4. package/package.json +10 -7
  5. package/prompts/Development_Process.md +57 -35
  6. package/prompts/Jaypie_CDK_Constructs_and_Patterns.md +143 -19
  7. package/prompts/Jaypie_Express_Package.md +408 -0
  8. package/prompts/Jaypie_Init_Express_on_Lambda.md +66 -38
  9. package/prompts/Jaypie_Init_Lambda_Package.md +202 -83
  10. package/prompts/Jaypie_Init_Project_Subpackage.md +21 -26
  11. package/prompts/Jaypie_Legacy_Patterns.md +4 -0
  12. package/prompts/Templates_CDK_Subpackage.md +113 -0
  13. package/prompts/Templates_Express_Subpackage.md +183 -0
  14. package/prompts/Templates_Project_Monorepo.md +326 -0
  15. package/prompts/Templates_Project_Subpackage.md +93 -0
  16. package/LICENSE.txt +0 -21
  17. package/prompts/Jaypie_Mongoose_Models_Package.md +0 -231
  18. package/prompts/Jaypie_Mongoose_with_Express_CRUD.md +0 -1000
  19. package/prompts/templates/cdk-subpackage/bin/cdk.ts +0 -11
  20. package/prompts/templates/cdk-subpackage/cdk.json +0 -19
  21. package/prompts/templates/cdk-subpackage/lib/cdk-app.ts +0 -41
  22. package/prompts/templates/cdk-subpackage/lib/cdk-infrastructure.ts +0 -15
  23. package/prompts/templates/express-subpackage/index.ts +0 -8
  24. package/prompts/templates/express-subpackage/src/app.ts +0 -18
  25. package/prompts/templates/express-subpackage/src/handler.config.ts +0 -44
  26. package/prompts/templates/express-subpackage/src/routes/resource/__tests__/resourceGet.route.spec.ts +0 -29
  27. package/prompts/templates/express-subpackage/src/routes/resource/resourceGet.route.ts +0 -22
  28. package/prompts/templates/express-subpackage/src/routes/resource.router.ts +0 -11
  29. package/prompts/templates/express-subpackage/src/types/express.ts +0 -9
  30. package/prompts/templates/project-monorepo/.vscode/settings.json +0 -72
  31. package/prompts/templates/project-monorepo/eslint.config.mjs +0 -1
  32. package/prompts/templates/project-monorepo/gitignore +0 -11
  33. package/prompts/templates/project-monorepo/package.json +0 -20
  34. package/prompts/templates/project-monorepo/tsconfig.base.json +0 -18
  35. package/prompts/templates/project-monorepo/tsconfig.json +0 -6
  36. package/prompts/templates/project-monorepo/vitest.workspace.js +0 -3
  37. package/prompts/templates/project-subpackage/package.json +0 -16
  38. package/prompts/templates/project-subpackage/tsconfig.json +0 -11
  39. package/prompts/templates/project-subpackage/vite.config.ts +0 -21
  40. package/prompts/templates/project-subpackage/vitest.config.ts +0 -7
  41. package/prompts/templates/project-subpackage/vitest.setup.ts +0 -6
@@ -33,10 +33,13 @@ new JaypieLambda(this, "MyFunction", {
33
33
  ```
34
34
 
35
35
  Features:
36
- - Automatic Datadog integration when `DATADOG_API_KEY_ARN` exists
36
+ - Automatic Datadog integration when `DATADOG_API_KEY_ARN` or `CDK_ENV_DATADOG_API_KEY_ARN` exists
37
37
  - Default environment variables from `PROJECT_*` settings
38
- - Provisioned concurrency support
39
- - Secrets management via `JaypieEnvSecret`
38
+ - Provisioned concurrency support via `provisionedConcurrentExecutions`
39
+ - Secrets management via `secrets` array (JaypieEnvSecret[])
40
+ - Direct secret integration via `envSecrets` object
41
+ - Parameter Store/Secrets Manager layer via `paramsAndSecrets`
42
+ - VPC, security groups, filesystem, and all standard Lambda configuration options
40
43
 
41
44
  ### Queue-Lambda Patterns
42
45
 
@@ -45,18 +48,33 @@ Use `JaypieQueuedLambda` for SQS-triggered Lambdas:
45
48
  new JaypieQueuedLambda(this, "Worker", {
46
49
  code: "dist",
47
50
  fifo: true,
48
- batchSize: 10
51
+ batchSize: 10,
52
+ visibilityTimeout: Duration.seconds(900)
49
53
  });
50
54
  ```
51
55
 
56
+ Features:
57
+ - Auto-creates SQS queue and connects to Lambda
58
+ - Implements both `lambda.IFunction` and `sqs.IQueue` interfaces
59
+ - Auto-injects `CDK_ENV_QUEUE_URL` environment variable
60
+ - Grants consume and send permissions to Lambda
61
+
52
62
  Use `JaypieBucketQueuedLambda` for S3-triggered processing:
53
63
  ```typescript
54
64
  new JaypieBucketQueuedLambda(this, "Processor", {
55
65
  code: "dist",
56
- bucketName: "my-bucket"
66
+ bucketName: "my-bucket",
67
+ bucketOptions: { versioned: true } // Optional S3 configuration
57
68
  });
58
69
  ```
59
70
 
71
+ Features:
72
+ - Extends `JaypieQueuedLambda` with S3 bucket and event notifications
73
+ - Forces non-FIFO queue (S3 limitation)
74
+ - Auto-injects `CDK_ENV_BUCKET_NAME` environment variable
75
+ - Grants read/write permissions to Lambda
76
+ - Implements `s3.IBucket` interface
77
+
60
78
  ### API Gateway
61
79
 
62
80
  Use `JaypieApiGateway` for REST APIs with custom domains:
@@ -101,22 +119,37 @@ new JaypieEnvSecret(this, "API_KEY");
101
119
  // Explicit configuration
102
120
  new JaypieEnvSecret(this, "ApiKey", {
103
121
  envKey: "API_KEY",
104
- provider: true, // Exports for other stacks
105
- consumer: false // Imports from provider stack
122
+ provider: true, // Exports for other stacks (default: PROJECT_ENV=sandbox)
123
+ consumer: false, // Imports from provider stack (default: PROJECT_ENV=personal)
124
+ value: "secret-value", // Direct value (alternative to envKey)
125
+ generateSecretString: {}, // Auto-generate secret
106
126
  });
107
127
  ```
108
128
 
129
+ Provider/consumer pattern:
130
+ - Provider stacks (sandbox) create and export secrets
131
+ - Consumer stacks (personal/ephemeral) import secrets by name
132
+ - Export name format: `env-${PROJECT_ENV}-${PROJECT_KEY}-${id}`
133
+
109
134
  ### Web Hosting
110
135
 
111
136
  Use `JaypieWebDeploymentBucket` for static sites:
112
137
  ```typescript
113
138
  new JaypieWebDeploymentBucket(this, "WebSite", {
114
139
  host: "www.example.com",
115
- zone: "example.com"
140
+ zone: "example.com",
141
+ certificate: true // Creates new cert; can pass ICertificate or false
116
142
  });
117
143
  ```
118
144
 
119
- Creates S3 bucket, CloudFront distribution, and DNS records.
145
+ Features:
146
+ - Creates S3 bucket with website hosting enabled
147
+ - Creates CloudFront distribution with SSL certificate
148
+ - Creates Route53 DNS records
149
+ - Auto-creates GitHub deployment role when `CDK_ENV_REPO` is set
150
+ - Production environments get optimized caching (index.html excluded)
151
+ - Implements `s3.IBucket` interface
152
+ - Can use `CDK_ENV_WEB_HOST` or `CDK_ENV_WEB_SUBDOMAIN` + hosted zone
120
153
 
121
154
  ### Hosted Zones
122
155
 
@@ -130,32 +163,123 @@ new JaypieHostedZone(this, "Zone", {
130
163
  ## Environment Variables
131
164
 
132
165
  Configure constructs via environment:
133
- - `CDK_ENV_API_HOST_NAME`: API domain name
166
+
167
+ ### API Configuration
168
+ - `CDK_ENV_API_HOST_NAME`: Full API domain name
169
+ - `CDK_ENV_API_SUBDOMAIN`: API subdomain (combined with CDK_ENV_API_HOSTED_ZONE)
134
170
  - `CDK_ENV_API_HOSTED_ZONE`: API hosted zone
135
- - `CDK_ENV_WEB_SUBDOMAIN`: Web subdomain
171
+
172
+ ### Web Configuration
173
+ - `CDK_ENV_WEB_HOST`: Full web domain name
174
+ - `CDK_ENV_WEB_SUBDOMAIN`: Web subdomain (combined with CDK_ENV_WEB_HOSTED_ZONE)
136
175
  - `CDK_ENV_WEB_HOSTED_ZONE`: Web hosted zone
137
- - `CDK_ENV_REPO`: GitHub repository for deployment roles
138
- - `DATADOG_API_KEY_ARN`: Datadog API key secret ARN
139
- - `PROJECT_ENV`: Environment name (sandbox, production)
176
+
177
+ ### General DNS
178
+ - `CDK_ENV_HOSTED_ZONE`: Fallback hosted zone (used by API and Web if specific zones not set)
179
+
180
+ ### Deployment
181
+ - `CDK_ENV_REPO`: GitHub repository for deployment roles (format: owner/repo)
182
+
183
+ ### Datadog Integration
184
+ - `DATADOG_API_KEY_ARN`: Datadog API key secret ARN (primary)
185
+ - `CDK_ENV_DATADOG_API_KEY_ARN`: Datadog API key secret ARN (fallback)
186
+ - `CDK_ENV_DATADOG_ROLE_ARN`: Datadog IAM role ARN for extended permissions
187
+
188
+ ### Project Configuration
189
+ - `PROJECT_ENV`: Environment name (sandbox, production, personal, etc.)
140
190
  - `PROJECT_KEY`: Project identifier
141
191
  - `PROJECT_SERVICE`: Service name
142
192
  - `PROJECT_SPONSOR`: Cost allocation tag
193
+ - `PROJECT_NONCE`: Unique identifier for ephemeral builds
194
+
195
+ ### Infrastructure Tracking
196
+ - `CDK_ENV_INFRASTRUCTURE_STACK_SHA`: Git SHA for infrastructure stack tagging
197
+
198
+ ### Auto-Injected (Set by Constructs)
199
+ - `CDK_ENV_BUCKET_NAME`: S3 bucket name (set by JaypieBucketQueuedLambda)
200
+ - `CDK_ENV_QUEUE_URL`: SQS queue URL (set by JaypieQueuedLambda)
143
201
 
144
202
  ## Helper Functions
145
203
 
146
204
  Use helper utilities:
147
205
  ```typescript
148
- import { constructStackName, constructEnvName, isEnv } from "@jaypie/constructs/helpers";
206
+ import { constructStackName, constructEnvName, isEnv } from "@jaypie/constructs";
149
207
 
150
208
  const stackName = constructStackName("app"); // project-env-app
151
209
  const resourceName = constructEnvName("bucket"); // project-env-bucket
152
210
  const isProduction = isEnv("production");
153
211
  ```
154
212
 
213
+ ## Additional Helper Functions
214
+
215
+ Available from `@jaypie/constructs`:
216
+
217
+ ```typescript
218
+ import {
219
+ isProductionEnv,
220
+ isSandboxEnv,
221
+ jaypieLambdaEnv,
222
+ constructTagger,
223
+ resolveHostedZone,
224
+ resolveDatadogLayers,
225
+ resolveDatadogForwarderFunction,
226
+ resolveDatadogLoggingDestination,
227
+ resolveParamsAndSecrets,
228
+ extendDatadogRole,
229
+ envHostname,
230
+ isValidHostname,
231
+ isValidSubdomain,
232
+ mergeDomain,
233
+ } from "@jaypie/constructs";
234
+ ```
235
+
236
+ Common usage:
237
+ - `isProductionEnv()`: Returns true if PROJECT_ENV is production
238
+ - `isSandboxEnv()`: Returns true if PROJECT_ENV is sandbox
239
+ - `jaypieLambdaEnv({ initialEnvironment })`: Merges PROJECT_* vars into Lambda env
240
+ - `resolveHostedZone(scope, { zone })`: Gets IHostedZone from string or object
241
+ - `extendDatadogRole(lambda)`: Adds Datadog IAM permissions when CDK_ENV_DATADOG_ROLE_ARN set
242
+
243
+ ## Additional Constructs
244
+
245
+ Other constructs available but not commonly used:
246
+
247
+ ### Base and Infrastructure
248
+ - `JaypieStack`: Base stack with standard tagging and configuration
249
+
250
+ ### Specialized Secrets
251
+ - `JaypieDatadogSecret`: Datadog API key secret management
252
+ - `JaypieMongoDbSecret`: MongoDB connection string secret
253
+ - `JaypieOpenAiSecret`: OpenAI API key secret
254
+ - `JaypieTraceSigningKeySecret`: Trace signing key secret
255
+
256
+ ### DNS and Networking
257
+ - `JaypieDnsRecord`: Create individual DNS records in hosted zones
258
+
259
+ ### Deployment and CI/CD
260
+ - `JaypieGitHubDeployRole`: GitHub Actions OIDC deployment role
261
+
262
+ ### Event-Driven
263
+ - `JaypieEventsRule`: EventBridge rules with standard configuration
264
+
265
+ ### Advanced Features
266
+ - `JaypieNextJs`: Next.js application deployment (uses cdk-nextjs-standalone)
267
+ - `JaypieDatadogForwarder`: Datadog log forwarder Lambda setup
268
+ - `JaypieOrganizationTrail`: CloudTrail organization-wide trail
269
+ - `JaypieSsoPermissions`: AWS IAM Identity Center permission sets
270
+ - `JaypieSsoSyncApplication`: SSO sync application for Google Workspace
271
+ - `JaypieAccountLoggingBucket`: Account-level centralized logging bucket
272
+ - `JaypieDatadogBucket`: Datadog-specific S3 bucket
273
+ - `JaypieStaticWebBucket`: Static web bucket (simpler than JaypieWebDeploymentBucket)
274
+ - `JaypieDistribution`: CloudFront distribution construct
275
+
155
276
  ## Tagging Strategy
156
277
 
157
278
  Constructs apply standard tags:
158
- - `role`: Resource role (api, processing, networking, hosting)
159
- - `vendor`: External service provider
160
- - `service`: Service category
161
- - `sponsor`: Cost allocation
279
+ - `role`: Resource role (api, processing, networking, hosting, deploy, monitoring, security, storage, stack)
280
+ - `vendor`: External service provider (auth0, datadog, mongodb, openai, knowtrace)
281
+ - `service`: Service category (datadog, infrastructure, libraries, sso, trace)
282
+ - `sponsor`: Cost allocation
283
+ - `project`: Project identifier (from PROJECT_KEY)
284
+ - `env`: Environment name (from PROJECT_ENV)
285
+ - `stackSha`: Git SHA for infrastructure stacks (from CDK_ENV_INFRASTRUCTURE_STACK_SHA)
@@ -0,0 +1,408 @@
1
+ ---
2
+ description: Complete guide to using Jaypie Express features including expressHandler, CORS, lifecycle hooks, and pre-built routes
3
+ globs: packages/express/**
4
+ ---
5
+
6
+ # Jaypie Express Package
7
+
8
+ Jaypie provides Express utilities through `@jaypie/express` (also available via `jaypie`). The main export is `expressHandler`, a function that wraps Express route handlers to add error handling, logging, lifecycle hooks, and automatic response formatting.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install jaypie
14
+ # or
15
+ npm install @jaypie/express
16
+ ```
17
+
18
+ ## expressHandler
19
+
20
+ Wraps Express route handlers with error handling, logging, and lifecycle management.
21
+
22
+ ### Basic Usage
23
+
24
+ ```typescript
25
+ import { expressHandler } from "jaypie";
26
+ import type { Request, Response } from "express";
27
+
28
+ const myRoute = expressHandler(async (req: Request, res: Response) => {
29
+ return { message: "Hello, World!" };
30
+ });
31
+
32
+ // Use in Express
33
+ app.get("/hello", myRoute);
34
+ ```
35
+
36
+ ### Return Value Handling
37
+
38
+ expressHandler automatically formats responses based on return values:
39
+
40
+ | Return Value | HTTP Status | Response |
41
+ |--------------|-------------|----------|
42
+ | Object | 200 | JSON body |
43
+ | Array | 200 | JSON body |
44
+ | String (JSON) | 200 | Parsed JSON |
45
+ | String (other) | 200 | Text body |
46
+ | Number | 200 | Sent via `res.send()` |
47
+ | `true` | 201 Created | Empty |
48
+ | `null`, `undefined`, `false` | 204 No Content | Empty |
49
+ | Object with `.json()` method | 200 | Result of `.json()` |
50
+
51
+ **Note:** If you call `res.json()`, `res.send()`, or `res.end()` directly in your handler, expressHandler will log a warning but respect your call. Prefer using return values instead.
52
+
53
+ ### Options
54
+
55
+ ```typescript
56
+ import { expressHandler } from "jaypie";
57
+ import type { ExpressHandlerOptions } from "jaypie";
58
+
59
+ const options: ExpressHandlerOptions = {
60
+ name: "myHandler", // Handler name for logging
61
+ chaos: "low", // Chaos testing level
62
+ unavailable: false, // Return 503 if true
63
+ setup: [], // Setup function(s)
64
+ teardown: [], // Teardown function(s)
65
+ validate: [], // Validation function(s)
66
+ locals: {}, // Values to set on req.locals
67
+ };
68
+
69
+ const handler = expressHandler(async (req, res) => {
70
+ return { success: true };
71
+ }, options);
72
+
73
+ // Alternative: options first
74
+ const handler2 = expressHandler(options, async (req, res) => {
75
+ return { success: true };
76
+ });
77
+ ```
78
+
79
+ ## Lifecycle Hooks
80
+
81
+ Lifecycle hooks execute in this order:
82
+ 1. Setup functions (in array order)
83
+ 2. Locals functions (in object key order, after all setup)
84
+ 3. Validate functions (in array order)
85
+ 4. Main handler
86
+ 5. Teardown functions (always run, even on error)
87
+
88
+ ### Setup Functions
89
+
90
+ Run before validation and the main handler. Use for initialization, authentication checks, or setting up request context.
91
+
92
+ ```typescript
93
+ import { expressHandler } from "jaypie";
94
+ import type { JaypieHandlerSetup } from "jaypie";
95
+
96
+ const authenticateUser: JaypieHandlerSetup = async (req, res) => {
97
+ const token = req.headers.authorization;
98
+ // Validate token, throw UnauthorizedError if invalid
99
+ };
100
+
101
+ const loadTenant: JaypieHandlerSetup = async (req, res) => {
102
+ req.locals.tenant = await getTenant(req.params.tenantId);
103
+ };
104
+
105
+ const handler = expressHandler(
106
+ async (req, res) => {
107
+ // req.locals.tenant is available here
108
+ return { tenant: req.locals.tenant };
109
+ },
110
+ {
111
+ setup: [authenticateUser, loadTenant],
112
+ }
113
+ );
114
+ ```
115
+
116
+ ### Teardown Functions
117
+
118
+ Run after the main handler completes. Teardown functions execute regardless of success or error, making them suitable for cleanup operations.
119
+
120
+ ```typescript
121
+ import type { JaypieHandlerTeardown } from "jaypie";
122
+
123
+ const closeConnection: JaypieHandlerTeardown = async (req, res) => {
124
+ await req.locals.dbConnection?.close();
125
+ };
126
+
127
+ const handler = expressHandler(
128
+ async (req, res) => {
129
+ req.locals.dbConnection = await openConnection();
130
+ return await doWork(req.locals.dbConnection);
131
+ },
132
+ {
133
+ teardown: closeConnection,
134
+ }
135
+ );
136
+ ```
137
+
138
+ ### Validation Functions
139
+
140
+ Run before the main handler. Return `true` to continue or `false`/throw to reject.
141
+
142
+ ```typescript
143
+ import { ForbiddenError } from "jaypie";
144
+ import type { JaypieHandlerValidate } from "jaypie";
145
+
146
+ const requireAdmin: JaypieHandlerValidate = (req, res) => {
147
+ if (!req.locals.user?.isAdmin) {
148
+ throw new ForbiddenError();
149
+ }
150
+ return true;
151
+ };
152
+
153
+ const validateBody: JaypieHandlerValidate = (req, res) => {
154
+ return req.body?.email && req.body?.name;
155
+ };
156
+
157
+ const handler = expressHandler(
158
+ async (req, res) => {
159
+ // Only runs if user is admin and body is valid
160
+ return { success: true };
161
+ },
162
+ {
163
+ validate: [requireAdmin, validateBody],
164
+ }
165
+ );
166
+ ```
167
+
168
+ ### Locals
169
+
170
+ Set values on `req.locals`. Values can be static or functions that receive `(req, res)`. Locals functions are called AFTER setup functions.
171
+
172
+ ```typescript
173
+ import type { ExpressHandlerLocals } from "jaypie";
174
+
175
+ const getUser: ExpressHandlerLocals = async (req, res) => {
176
+ return await User.findById(req.params.userId);
177
+ };
178
+
179
+ const handler = expressHandler(
180
+ async (req, res) => {
181
+ // Access via req.locals
182
+ console.log(req.locals.apiVersion); // "v1"
183
+ console.log(req.locals.user); // User object
184
+ return req.locals.user;
185
+ },
186
+ {
187
+ locals: {
188
+ apiVersion: "v1", // Static value
189
+ user: getUser, // Function called after setup
190
+ },
191
+ }
192
+ );
193
+ ```
194
+
195
+ ## CORS Helper
196
+
197
+ Configures CORS middleware using the `cors` npm package with automatic origin validation.
198
+
199
+ ```typescript
200
+ import { cors } from "jaypie";
201
+ import type { CorsConfig } from "jaypie";
202
+
203
+ // Default: uses BASE_URL or PROJECT_BASE_URL env vars
204
+ app.use(cors());
205
+
206
+ // Wildcard origin
207
+ app.use(cors({ origin: "*" }));
208
+
209
+ // Specific origin
210
+ app.use(cors({ origin: "https://example.com" }));
211
+
212
+ // Multiple origins
213
+ app.use(cors({ origin: ["https://example.com", "https://app.example.com"] }));
214
+
215
+ // Custom configuration
216
+ const corsConfig: CorsConfig = {
217
+ origin: "https://api.example.com",
218
+ overrides: {
219
+ // Additional options passed to the cors package
220
+ credentials: true,
221
+ },
222
+ };
223
+ app.use(cors(corsConfig));
224
+ ```
225
+
226
+ Environment variables:
227
+ - `BASE_URL` or `PROJECT_BASE_URL`: Default allowed origins
228
+ - `PROJECT_ENV=sandbox` or `PROJECT_SANDBOX_MODE=true`: Allows localhost origins (including ports)
229
+
230
+ ## Pre-built Routes
231
+
232
+ Ready-to-use route handlers for common responses.
233
+
234
+ ```typescript
235
+ import {
236
+ badRequestRoute, // 400 Bad Request
237
+ echoRoute, // 200 with request echo
238
+ forbiddenRoute, // 403 Forbidden
239
+ goneRoute, // 410 Gone
240
+ methodNotAllowedRoute, // 405 Method Not Allowed
241
+ noContentRoute, // 204 No Content
242
+ notFoundRoute, // 404 Not Found
243
+ notImplementedRoute, // 501 Not Implemented
244
+ } from "jaypie";
245
+
246
+ // Use as catch-all or placeholder routes
247
+ app.all("/deprecated/*", goneRoute);
248
+ app.use("*", notFoundRoute);
249
+
250
+ // Echo route for debugging
251
+ app.get("/debug/echo", echoRoute);
252
+ ```
253
+
254
+ ## HTTP Code Handler
255
+
256
+ Create custom HTTP status code handlers.
257
+
258
+ ```typescript
259
+ import { expressHttpCodeHandler, HTTP } from "jaypie";
260
+
261
+ // Returns 200 OK with empty body
262
+ const okRoute = expressHttpCodeHandler(HTTP.CODE.OK);
263
+
264
+ // Returns 202 Accepted
265
+ const acceptedRoute = expressHttpCodeHandler(202, { name: "accepted" });
266
+
267
+ app.post("/jobs", acceptedRoute);
268
+ ```
269
+
270
+ ## Error Handling
271
+
272
+ Throw Jaypie errors for proper HTTP responses.
273
+
274
+ ```typescript
275
+ import {
276
+ expressHandler,
277
+ BadRequestError,
278
+ NotFoundError,
279
+ UnauthorizedError,
280
+ ForbiddenError,
281
+ InternalError,
282
+ log,
283
+ } from "jaypie";
284
+
285
+ const handler = expressHandler(async (req, res) => {
286
+ const item = await findItem(req.params.id);
287
+
288
+ if (!item) {
289
+ log.warn("Item not found");
290
+ throw new NotFoundError();
291
+ }
292
+
293
+ if (!canAccess(req.user, item)) {
294
+ throw new ForbiddenError();
295
+ }
296
+
297
+ return item;
298
+ });
299
+ ```
300
+
301
+ Errors return JSON:API compliant error responses:
302
+
303
+ ```json
304
+ {
305
+ "errors": [{
306
+ "status": 404,
307
+ "title": "Not Found",
308
+ "detail": "The requested resource was not found"
309
+ }]
310
+ }
311
+ ```
312
+
313
+ ## TypeScript Types
314
+
315
+ All lifecycle function types are exported for type safety:
316
+
317
+ ```typescript
318
+ import type {
319
+ ExpressHandlerOptions,
320
+ ExpressHandlerLocals,
321
+ JaypieHandlerSetup,
322
+ JaypieHandlerTeardown,
323
+ JaypieHandlerValidate,
324
+ CorsConfig,
325
+ } from "jaypie";
326
+ ```
327
+
328
+ ## Complete Example
329
+
330
+ ```typescript
331
+ import express from "express";
332
+ import {
333
+ cors,
334
+ expressHandler,
335
+ notFoundRoute,
336
+ NotFoundError,
337
+ ForbiddenError,
338
+ UnauthorizedError,
339
+ log,
340
+ } from "jaypie";
341
+ import type {
342
+ JaypieHandlerSetup,
343
+ JaypieHandlerValidate,
344
+ ExpressHandlerLocals,
345
+ } from "jaypie";
346
+
347
+ const app = express();
348
+ app.use(express.json());
349
+ app.use(cors());
350
+
351
+ // Lifecycle functions
352
+ const authenticate: JaypieHandlerSetup = async (req, res) => {
353
+ const token = req.headers.authorization?.replace("Bearer ", "");
354
+ if (!token) throw new UnauthorizedError();
355
+ req.locals.user = await verifyToken(token);
356
+ };
357
+
358
+ const requireOwner: JaypieHandlerValidate = (req, res) => {
359
+ return req.locals.resource?.ownerId === req.locals.user?.id;
360
+ };
361
+
362
+ const loadResource: ExpressHandlerLocals = async (req, res) => {
363
+ const resource = await Resource.findById(req.params.id);
364
+ if (!resource) throw new NotFoundError();
365
+ return resource;
366
+ };
367
+
368
+ // Route handler
369
+ const updateResource = expressHandler(
370
+ async (req, res) => {
371
+ const { resource, user } = req.locals;
372
+
373
+ resource.name = req.body.name;
374
+ resource.updatedBy = user.id;
375
+ await resource.save();
376
+
377
+ log.trace("Resource updated");
378
+ return resource;
379
+ },
380
+ {
381
+ name: "updateResource",
382
+ setup: authenticate,
383
+ validate: requireOwner,
384
+ locals: {
385
+ resource: loadResource,
386
+ },
387
+ }
388
+ );
389
+
390
+ app.put("/resources/:id", updateResource);
391
+ app.use("*", notFoundRoute);
392
+
393
+ export default app;
394
+ ```
395
+
396
+ ## Response Headers
397
+
398
+ expressHandler automatically sets these headers:
399
+ - `X-Powered-By: @jaypie/express` (always set, overrides Express default)
400
+ - `X-Project-Handler: {name}` (when name option is provided)
401
+ - `X-Project-Invocation: {uuid}` (request tracking ID, always set)
402
+ - `X-Project-Environment: {env}` (when PROJECT_ENV is set)
403
+ - `X-Project-Key: {key}` (when PROJECT_KEY is set)
404
+ - `X-Project-Version: {version}` (when PROJECT_VERSION is set or version option provided)
405
+
406
+ ## Datadog Integration
407
+
408
+ When Datadog environment variables are configured, expressHandler automatically submits metrics for each request including status code and path.