deploy-bbc 0.0.1
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/CLAUDE.md +329 -0
- package/README.md +0 -0
- package/cli/README.md +0 -0
- package/cli/package.json +43 -0
- package/cli/src/cli/index.ts +449 -0
- package/cli/src/helpers/create-project.ts +66 -0
- package/cli/src/helpers/generate-docker-compose.ts +133 -0
- package/cli/src/helpers/generate-dockerfile.ts +45 -0
- package/cli/src/helpers/init-git.ts +33 -0
- package/cli/src/helpers/install-dependencies.ts +28 -0
- package/cli/src/helpers/log-next-steps.ts +84 -0
- package/cli/src/helpers/scaffold-project.ts +62 -0
- package/cli/src/index.ts +18 -0
- package/cli/src/installers/ai.ts +123 -0
- package/cli/src/installers/auth.ts +132 -0
- package/cli/src/installers/base.ts +16 -0
- package/cli/src/installers/cloud.ts +127 -0
- package/cli/src/installers/database.ts +212 -0
- package/cli/src/installers/docs.ts +93 -0
- package/cli/src/installers/email.ts +119 -0
- package/cli/src/installers/env-variables.ts +27 -0
- package/cli/src/installers/index.ts +145 -0
- package/cli/src/installers/observability.ts +103 -0
- package/cli/src/installers/queue.ts +103 -0
- package/cli/src/installers/ratelimit.ts +98 -0
- package/cli/src/installers/realtime.ts +79 -0
- package/cli/src/installers/testing.ts +88 -0
- package/cli/src/installers/validation.ts +85 -0
- package/cli/src/templates/base/.env.example +3 -0
- package/cli/src/templates/base/README.md +31 -0
- package/cli/src/templates/base/package.json +20 -0
- package/cli/src/templates/base/src/config/index.ts +8 -0
- package/cli/src/templates/base/src/index.ts +26 -0
- package/cli/src/templates/base/src/middleware/error.ts +13 -0
- package/cli/src/templates/base/src/middleware/logger.ts +8 -0
- package/cli/src/templates/base/src/routes/index.ts +12 -0
- package/cli/src/templates/base/src/types/index.ts +2 -0
- package/cli/src/templates/base/src/utils/env.ts +5 -0
- package/cli/src/templates/base/tsconfig.json +20 -0
- package/cli/src/templates/base-bun-native/.env.example +3 -0
- package/cli/src/templates/base-bun-native/README.md +31 -0
- package/cli/src/templates/base-bun-native/package.json +19 -0
- package/cli/src/templates/base-bun-native/src/config/index.ts +8 -0
- package/cli/src/templates/base-bun-native/src/index.ts +50 -0
- package/cli/src/templates/base-bun-native/src/middleware/error.ts +20 -0
- package/cli/src/templates/base-bun-native/src/middleware/logger.ts +6 -0
- package/cli/src/templates/base-bun-native/src/routes/index.ts +21 -0
- package/cli/src/templates/base-bun-native/src/types/index.ts +2 -0
- package/cli/src/templates/base-bun-native/src/utils/env.ts +5 -0
- package/cli/src/templates/base-bun-native/tsconfig.json +20 -0
- package/cli/src/templates/base-express/.env.example +3 -0
- package/cli/src/templates/base-express/README.md +31 -0
- package/cli/src/templates/base-express/package.json +21 -0
- package/cli/src/templates/base-express/src/config/index.ts +8 -0
- package/cli/src/templates/base-express/src/index.ts +27 -0
- package/cli/src/templates/base-express/src/middleware/error.ts +15 -0
- package/cli/src/templates/base-express/src/middleware/logger.ts +12 -0
- package/cli/src/templates/base-express/src/routes/index.ts +12 -0
- package/cli/src/templates/base-express/src/types/index.ts +2 -0
- package/cli/src/templates/base-express/src/utils/env.ts +5 -0
- package/cli/src/templates/base-express/tsconfig.json +20 -0
- package/cli/src/templates/extras/ai/anthropic/src/routes/ai/claude.ts +0 -0
- package/cli/src/templates/extras/ai/anthropic/src/services/ai/anthropic.ts +0 -0
- package/cli/src/templates/extras/ai/gemini/src/services/ai/gemini.ts +0 -0
- package/cli/src/templates/extras/ai/openai/src/routes/ai/chat.ts +0 -0
- package/cli/src/templates/extras/ai/openai/src/services/ai/openai.ts +0 -0
- package/cli/src/templates/extras/ai/vercel-ai/src/routes/ai/generate.ts +0 -0
- package/cli/src/templates/extras/ai/vercel-ai/src/routes/ai/stream.ts +0 -0
- package/cli/src/templates/extras/ai/vercel-ai/src/services/ai/index.ts +0 -0
- package/cli/src/templates/extras/auth/jwt/src/middleware/auth.ts +0 -0
- package/cli/src/templates/extras/auth/jwt/src/routes/auth.ts +0 -0
- package/cli/src/templates/extras/auth/jwt/src/utils/jwt.ts +0 -0
- package/cli/src/templates/extras/auth/oauth/src/config/oauth.ts +0 -0
- package/cli/src/templates/extras/auth/oauth/src/routes/auth.ts +0 -0
- package/cli/src/templates/extras/auth/session/src/config/session.ts +0 -0
- package/cli/src/templates/extras/auth/session/src/middleware/session.ts +0 -0
- package/cli/src/templates/extras/cloud/aws/src/services/aws/s3.ts +0 -0
- package/cli/src/templates/extras/cloud/aws/src/services/aws/ses.ts +0 -0
- package/cli/src/templates/extras/cloud/azure/src/services/azure/blob.ts +0 -0
- package/cli/src/templates/extras/cloud/cloudflare-r2/src/services/cloudflare/r2.ts +0 -0
- package/cli/src/templates/extras/cloud/gcp/src/services/gcp/storage.ts +0 -0
- package/cli/src/templates/extras/database/mongodb/src/db/index.ts +0 -0
- package/cli/src/templates/extras/database/mongodb/src/db/models/user.model.ts +0 -0
- package/cli/src/templates/extras/database/mysql/drizzle.config.ts +0 -0
- package/cli/src/templates/extras/database/postgres/src/db/index.ts +0 -0
- package/cli/src/templates/extras/database/redis/src/db/redis.ts +0 -0
- package/cli/src/templates/extras/docs/scalar/src/openapi/index.ts +0 -0
- package/cli/src/templates/extras/docs/scalar/src/routes/docs.ts +0 -0
- package/cli/src/templates/extras/docs/swagger/src/openapi/index.ts +0 -0
- package/cli/src/templates/extras/docs/swagger/src/routes/docs.ts +0 -0
- package/cli/src/templates/extras/email/nodemailer/src/services/email/nodemailer.ts +0 -0
- package/cli/src/templates/extras/email/resend/src/services/email/resend.ts +0 -0
- package/cli/src/templates/extras/email/resend/src/templates/email/welcome.ts +0 -0
- package/cli/src/templates/extras/email/sendgrid/src/services/email/sendgrid.ts +0 -0
- package/cli/src/templates/extras/observability/logtail/src/config/logger.ts +0 -0
- package/cli/src/templates/extras/observability/sentry/src/config/sentry.ts +0 -0
- package/cli/src/templates/extras/observability/sentry/src/middleware/sentry.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/queue/index.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/queue/jobs/email.job.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/queue/processors/email.processor.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/routes/queue.ts +0 -0
- package/cli/src/templates/extras/queue/inngest/src/inngest/client.ts +0 -0
- package/cli/src/templates/extras/queue/inngest/src/inngest/functions/email.ts +0 -0
- package/cli/src/templates/extras/queue/inngest/src/routes/inngest.ts +0 -0
- package/cli/src/templates/extras/realtime/socketio/src/socket/handlers.ts +0 -0
- package/cli/src/templates/extras/realtime/socketio/src/socket/index.ts +0 -0
- package/cli/src/templates/extras/realtime/sse/src/routes/sse.ts +0 -0
- package/cli/src/templates/extras/testing/vitest/src/__tests__/example.test.ts +0 -0
- package/cli/src/templates/extras/testing/vitest/src/__tests__/setup.ts +0 -0
- package/cli/src/templates/extras/testing/vitest/vitest.config.ts +0 -0
- package/cli/src/templates/extras/validation/yup/src/middleware/index.ts +1 -0
- package/cli/src/templates/extras/validation/yup/src/middleware/validate.ts +83 -0
- package/cli/src/templates/extras/validation/yup/src/routes/users.ts +132 -0
- package/cli/src/templates/extras/validation/zod/src/middleware/index.ts +1 -0
- package/cli/src/templates/extras/validation/zod/src/middleware/validate.ts +80 -0
- package/cli/src/templates/extras/validation/zod/src/routes/users.ts +128 -0
- package/cli/src/types/index.ts +126 -0
- package/cli/src/utils/add-package-dependency.ts +56 -0
- package/cli/src/utils/dependency-version-map.ts +85 -0
- package/cli/src/utils/logger.ts +19 -0
- package/cli/src/utils/parse-name-and-path.ts +55 -0
- package/cli/src/utils/render-title.ts +11 -0
- package/cli/tsconfig.json +35 -0
- package/package.json +20 -0
- package/prettier.config.mjs +0 -0
- package/test-cli.sh +56 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { config } from "./config/index.js";
|
|
2
|
+
import { logger } from "./middleware/logger.js";
|
|
3
|
+
import { errorHandler } from "./middleware/error.js";
|
|
4
|
+
import { router } from "./routes/index.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Simple router matcher for Bun native server
|
|
8
|
+
*/
|
|
9
|
+
function match_route(pathname: string, method: string): Response | null {
|
|
10
|
+
return router(pathname, method);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const server = Bun.serve({
|
|
14
|
+
port: config.port,
|
|
15
|
+
async fetch(req) {
|
|
16
|
+
const url = new URL(req.url);
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Health check
|
|
21
|
+
if (url.pathname === "/health" && req.method === "GET") {
|
|
22
|
+
const response = Response.json({
|
|
23
|
+
status: "ok",
|
|
24
|
+
timestamp: new Date().toISOString(),
|
|
25
|
+
});
|
|
26
|
+
logger(req, response, Date.now() - start);
|
|
27
|
+
return response;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Match routes
|
|
31
|
+
const response = match_route(url.pathname, req.method);
|
|
32
|
+
if (response) {
|
|
33
|
+
logger(req, response, Date.now() - start);
|
|
34
|
+
return response;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 404 Not Found
|
|
38
|
+
const notFoundResponse = Response.json(
|
|
39
|
+
{ error: "Not Found" },
|
|
40
|
+
{ status: 404 }
|
|
41
|
+
);
|
|
42
|
+
logger(req, notFoundResponse, Date.now() - start);
|
|
43
|
+
return notFoundResponse;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return errorHandler(error, req);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log(`🚀 Server running on http://localhost:${server.port}`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handler for Bun native server
|
|
3
|
+
*/
|
|
4
|
+
export function errorHandler(error: unknown, req: Request): Response {
|
|
5
|
+
console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`, error);
|
|
6
|
+
|
|
7
|
+
const status = error instanceof Error && "status" in error
|
|
8
|
+
? (error as Error & { status: number }).status
|
|
9
|
+
: 500;
|
|
10
|
+
|
|
11
|
+
const message = error instanceof Error ? error.message : "Internal Server Error";
|
|
12
|
+
|
|
13
|
+
return Response.json(
|
|
14
|
+
{
|
|
15
|
+
error: message,
|
|
16
|
+
status,
|
|
17
|
+
},
|
|
18
|
+
{ status }
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple router for Bun native server
|
|
3
|
+
* Add your routes here
|
|
4
|
+
*/
|
|
5
|
+
export function router(pathname: string, method: string): Response | null {
|
|
6
|
+
// GET /
|
|
7
|
+
if (pathname === "/" && method === "GET") {
|
|
8
|
+
return Response.json({
|
|
9
|
+
message: "Welcome to your Bun backend!",
|
|
10
|
+
docs: "/docs",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Add more routes here
|
|
15
|
+
// Example:
|
|
16
|
+
// if (pathname === "/users" && method === "GET") {
|
|
17
|
+
// return Response.json({ users: [] });
|
|
18
|
+
// }
|
|
19
|
+
|
|
20
|
+
return null; // No route matched
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ESNext"],
|
|
7
|
+
"types": ["bun-types"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"outDir": "./dist",
|
|
16
|
+
"rootDir": "./src"
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# My Backend App
|
|
2
|
+
|
|
3
|
+
Production-ready Bun backend with TypeScript and Hono.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
bun install
|
|
10
|
+
|
|
11
|
+
# Copy environment variables
|
|
12
|
+
cp .env.example .env
|
|
13
|
+
|
|
14
|
+
# Run development server
|
|
15
|
+
bun run dev
|
|
16
|
+
|
|
17
|
+
# Build for production
|
|
18
|
+
bun run build
|
|
19
|
+
|
|
20
|
+
# Run production server
|
|
21
|
+
bun run start
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## API Endpoints
|
|
25
|
+
|
|
26
|
+
- `GET /` - Welcome message
|
|
27
|
+
- `GET /health` - Health check
|
|
28
|
+
|
|
29
|
+
## Documentation
|
|
30
|
+
|
|
31
|
+
Visit `/docs` for API documentation (if Swagger/Scalar is installed).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-backend-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-ready backend with Express and TypeScript",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "bun --watch src/index.ts",
|
|
8
|
+
"start": "bun src/index.ts",
|
|
9
|
+
"build": "bun build src/index.ts --outdir dist --target bun",
|
|
10
|
+
"type-check": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"express": "^4.18.2",
|
|
14
|
+
"dotenv": "^16.3.1"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/bun": "latest",
|
|
18
|
+
"@types/express": "^4.17.21",
|
|
19
|
+
"typescript": "^5.3.3"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { logger as loggerMiddleware } from "./middleware/logger.js";
|
|
3
|
+
import { errorHandler } from "./middleware/error.js";
|
|
4
|
+
import routes from "./routes/index.js";
|
|
5
|
+
import { config } from "./config/index.js";
|
|
6
|
+
|
|
7
|
+
const app = express();
|
|
8
|
+
|
|
9
|
+
// Middleware
|
|
10
|
+
app.use(express.json());
|
|
11
|
+
app.use(express.urlencoded({ extended: true }));
|
|
12
|
+
app.use(loggerMiddleware);
|
|
13
|
+
|
|
14
|
+
// Routes
|
|
15
|
+
app.use("/", routes);
|
|
16
|
+
|
|
17
|
+
// Health check
|
|
18
|
+
app.get("/health", (req, res) => {
|
|
19
|
+
res.json({ status: "ok", timestamp: new Date().toISOString() });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Error handler (must be last)
|
|
23
|
+
app.use(errorHandler);
|
|
24
|
+
|
|
25
|
+
app.listen(config.port, () => {
|
|
26
|
+
console.log(`🚀 Server running on http://localhost:${config.port}`);
|
|
27
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
|
|
3
|
+
export const errorHandler = (
|
|
4
|
+
err: Error & { status?: number },
|
|
5
|
+
req: Request,
|
|
6
|
+
res: Response,
|
|
7
|
+
next: NextFunction
|
|
8
|
+
) => {
|
|
9
|
+
console.error(`Error: ${err.message}`, err);
|
|
10
|
+
|
|
11
|
+
res.status(err.status || 500).json({
|
|
12
|
+
error: err.message || "Internal Server Error",
|
|
13
|
+
status: err.status || 500,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
|
|
3
|
+
export const logger = (req: Request, res: Response, next: NextFunction) => {
|
|
4
|
+
const start = Date.now();
|
|
5
|
+
|
|
6
|
+
res.on("finish", () => {
|
|
7
|
+
const ms = Date.now() - start;
|
|
8
|
+
console.log(`${req.method} ${req.url} - ${res.statusCode} (${ms}ms)`);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
next();
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ESNext"],
|
|
7
|
+
"types": ["bun-types"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"outDir": "./dist",
|
|
16
|
+
"rootDir": "./src"
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validate, get_validated, type ValidateTarget } from "./validate.js";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Context, Next } from "hono";
|
|
2
|
+
import * as yup from "yup";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validation middleware for Hono using Yup schemas
|
|
6
|
+
* Validates request body, query parameters, or route params
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type ValidateTarget = "body" | "query" | "param";
|
|
10
|
+
|
|
11
|
+
interface ValidationOptions {
|
|
12
|
+
target: ValidateTarget;
|
|
13
|
+
schema: yup.AnySchema;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a validation middleware
|
|
18
|
+
* @param options - Validation configuration
|
|
19
|
+
* @returns Hono middleware function
|
|
20
|
+
*/
|
|
21
|
+
export function validate(options: ValidationOptions) {
|
|
22
|
+
return async (c: Context, next: Next) => {
|
|
23
|
+
try {
|
|
24
|
+
let data: unknown;
|
|
25
|
+
|
|
26
|
+
switch (options.target) {
|
|
27
|
+
case "body":
|
|
28
|
+
data = await c.req.json();
|
|
29
|
+
break;
|
|
30
|
+
case "query":
|
|
31
|
+
data = c.req.query();
|
|
32
|
+
break;
|
|
33
|
+
case "param":
|
|
34
|
+
data = c.req.param();
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
return c.json(
|
|
38
|
+
{ error: "Invalid validation target" },
|
|
39
|
+
400
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Validate data against schema
|
|
44
|
+
const validated = await options.schema.validate(data, {
|
|
45
|
+
abortEarly: false,
|
|
46
|
+
stripUnknown: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Store validated data in context for route handlers
|
|
50
|
+
c.set(`validated_${options.target}`, validated);
|
|
51
|
+
|
|
52
|
+
await next();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error instanceof yup.ValidationError) {
|
|
55
|
+
return c.json(
|
|
56
|
+
{
|
|
57
|
+
error: "Validation failed",
|
|
58
|
+
details: error.inner.map((err) => ({
|
|
59
|
+
path: err.path || "unknown",
|
|
60
|
+
message: err.message,
|
|
61
|
+
})),
|
|
62
|
+
},
|
|
63
|
+
400
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return c.json(
|
|
68
|
+
{ error: "Invalid request data" },
|
|
69
|
+
400
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Helper to get validated data from context
|
|
77
|
+
* @param c - Hono context
|
|
78
|
+
* @param target - Validation target (body/query/param)
|
|
79
|
+
* @returns Validated data
|
|
80
|
+
*/
|
|
81
|
+
export function get_validated<T>(c: Context, target: ValidateTarget): T {
|
|
82
|
+
return c.get(`validated_${target}`) as T;
|
|
83
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import * as yup from "yup";
|
|
3
|
+
import { validate, get_validated } from "../middleware/validate.js";
|
|
4
|
+
|
|
5
|
+
const users = new Hono();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Yup Schemas for validation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Schema for creating a new user
|
|
12
|
+
const create_user_schema = yup.object({
|
|
13
|
+
name: yup.string().required().min(2, "Name must be at least 2 characters"),
|
|
14
|
+
email: yup.string().required().email("Invalid email format"),
|
|
15
|
+
age: yup.number().integer().min(18, "Must be at least 18 years old").optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Schema for updating a user
|
|
19
|
+
const update_user_schema = yup.object({
|
|
20
|
+
name: yup.string().min(2).optional(),
|
|
21
|
+
email: yup.string().email().optional(),
|
|
22
|
+
age: yup.number().integer().min(18).optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Schema for query parameters
|
|
26
|
+
const list_users_query_schema = yup.object({
|
|
27
|
+
page: yup.number().transform((value, originalValue) => {
|
|
28
|
+
return originalValue ? parseInt(originalValue, 10) : undefined;
|
|
29
|
+
}).optional(),
|
|
30
|
+
limit: yup.number().transform((value, originalValue) => {
|
|
31
|
+
return originalValue ? parseInt(originalValue, 10) : undefined;
|
|
32
|
+
}).optional(),
|
|
33
|
+
search: yup.string().optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Schema for route params
|
|
37
|
+
const user_id_param_schema = yup.object({
|
|
38
|
+
id: yup.string().required().uuid("Invalid user ID format"),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Routes with validation
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
// GET /users - List users with query validation
|
|
46
|
+
users.get(
|
|
47
|
+
"/",
|
|
48
|
+
validate({ target: "query", schema: list_users_query_schema }),
|
|
49
|
+
(c) => {
|
|
50
|
+
const query = get_validated<yup.InferType<typeof list_users_query_schema>>(c, "query");
|
|
51
|
+
|
|
52
|
+
return c.json({
|
|
53
|
+
users: [],
|
|
54
|
+
pagination: {
|
|
55
|
+
page: query.page || 1,
|
|
56
|
+
limit: query.limit || 10,
|
|
57
|
+
search: query.search,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// POST /users - Create user with body validation
|
|
64
|
+
users.post(
|
|
65
|
+
"/",
|
|
66
|
+
validate({ target: "body", schema: create_user_schema }),
|
|
67
|
+
(c) => {
|
|
68
|
+
const body = get_validated<yup.InferType<typeof create_user_schema>>(c, "body");
|
|
69
|
+
|
|
70
|
+
// Your database logic here
|
|
71
|
+
const new_user = {
|
|
72
|
+
id: crypto.randomUUID(),
|
|
73
|
+
...body,
|
|
74
|
+
created_at: new Date().toISOString(),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return c.json(new_user, 201);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// GET /users/:id - Get user by ID with param validation
|
|
82
|
+
users.get(
|
|
83
|
+
"/:id",
|
|
84
|
+
validate({ target: "param", schema: user_id_param_schema }),
|
|
85
|
+
(c) => {
|
|
86
|
+
const params = get_validated<yup.InferType<typeof user_id_param_schema>>(c, "param");
|
|
87
|
+
|
|
88
|
+
// Your database logic here
|
|
89
|
+
const user = {
|
|
90
|
+
id: params.id,
|
|
91
|
+
name: "John Doe",
|
|
92
|
+
email: "john@example.com",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return c.json(user);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// PATCH /users/:id - Update user with param + body validation
|
|
100
|
+
users.patch(
|
|
101
|
+
"/:id",
|
|
102
|
+
validate({ target: "param", schema: user_id_param_schema }),
|
|
103
|
+
validate({ target: "body", schema: update_user_schema }),
|
|
104
|
+
(c) => {
|
|
105
|
+
const params = get_validated<yup.InferType<typeof user_id_param_schema>>(c, "param");
|
|
106
|
+
const body = get_validated<yup.InferType<typeof update_user_schema>>(c, "body");
|
|
107
|
+
|
|
108
|
+
// Your database logic here
|
|
109
|
+
const updated_user = {
|
|
110
|
+
id: params.id,
|
|
111
|
+
...body,
|
|
112
|
+
updated_at: new Date().toISOString(),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return c.json(updated_user);
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// DELETE /users/:id
|
|
120
|
+
users.delete(
|
|
121
|
+
"/:id",
|
|
122
|
+
validate({ target: "param", schema: user_id_param_schema }),
|
|
123
|
+
(c) => {
|
|
124
|
+
const params = get_validated<yup.InferType<typeof user_id_param_schema>>(c, "param");
|
|
125
|
+
|
|
126
|
+
return c.json({
|
|
127
|
+
message: `User ${params.id} deleted successfully`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
export default users;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validate, get_validated, type ValidateTarget } from "./validate.js";
|