postgresdk 0.1.1 → 0.1.2-alpha.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 +392 -7
- package/dist/cli.js +85 -0
- package/dist/emit-server-index.d.ts +5 -0
- package/dist/index.js +85 -0
- package/package.json +1 -1
package/README.md
CHANGED
@@ -1,6 +1,135 @@
|
|
1
1
|
# postgresdk
|
2
2
|
|
3
|
-
|
3
|
+
Turn your PostgreSQL database into a fully-typed, production-ready SDK in seconds.
|
4
|
+
|
5
|
+
## What You Get
|
6
|
+
|
7
|
+
Start with your existing PostgreSQL database:
|
8
|
+
|
9
|
+
```sql
|
10
|
+
CREATE TABLE authors (
|
11
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
12
|
+
name TEXT NOT NULL,
|
13
|
+
bio TEXT
|
14
|
+
);
|
15
|
+
|
16
|
+
CREATE TABLE books (
|
17
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
18
|
+
title TEXT NOT NULL,
|
19
|
+
author_id UUID REFERENCES authors(id),
|
20
|
+
published_at TIMESTAMPTZ
|
21
|
+
);
|
22
|
+
|
23
|
+
CREATE TABLE tags (
|
24
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
25
|
+
name TEXT UNIQUE NOT NULL
|
26
|
+
);
|
27
|
+
|
28
|
+
CREATE TABLE book_tags (
|
29
|
+
book_id UUID REFERENCES books(id),
|
30
|
+
tag_id UUID REFERENCES tags(id),
|
31
|
+
PRIMARY KEY (book_id, tag_id)
|
32
|
+
);
|
33
|
+
```
|
34
|
+
|
35
|
+
Run one command:
|
36
|
+
|
37
|
+
```bash
|
38
|
+
npx postgresdk
|
39
|
+
```
|
40
|
+
|
41
|
+
Get a complete, type-safe SDK with:
|
42
|
+
|
43
|
+
### 🎯 Client SDK with Full TypeScript Support
|
44
|
+
|
45
|
+
```typescript
|
46
|
+
import { SDK } from "./generated/client";
|
47
|
+
|
48
|
+
const sdk = new SDK({
|
49
|
+
baseUrl: "http://localhost:3000",
|
50
|
+
auth: { apiKey: "your-key" } // Optional auth
|
51
|
+
});
|
52
|
+
|
53
|
+
// ✅ Fully typed - autocomplete everything!
|
54
|
+
const author = await sdk.authors.create({
|
55
|
+
name: "Jane Austen",
|
56
|
+
bio: "English novelist known for social commentary"
|
57
|
+
});
|
58
|
+
|
59
|
+
// ✅ Type-safe relationships with eager loading
|
60
|
+
const booksWithAuthor = await sdk.books.list({
|
61
|
+
include: {
|
62
|
+
author: true, // 1:N relationship
|
63
|
+
tags: true // M:N relationship
|
64
|
+
}
|
65
|
+
});
|
66
|
+
|
67
|
+
// ✅ Complex nested queries
|
68
|
+
const authorsWithEverything = await sdk.authors.list({
|
69
|
+
include: {
|
70
|
+
books: {
|
71
|
+
include: {
|
72
|
+
tags: true
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
});
|
77
|
+
|
78
|
+
// ✅ Built-in pagination & filtering
|
79
|
+
const recentBooks = await sdk.books.list({
|
80
|
+
where: { published_at: { gte: "2024-01-01" } },
|
81
|
+
orderBy: "published_at",
|
82
|
+
order: "desc",
|
83
|
+
limit: 10
|
84
|
+
});
|
85
|
+
```
|
86
|
+
|
87
|
+
### 🚀 Production-Ready REST API
|
88
|
+
|
89
|
+
```typescript
|
90
|
+
import { Hono } from "hono";
|
91
|
+
import { Client } from "pg";
|
92
|
+
import { createRouter } from "./generated/server";
|
93
|
+
|
94
|
+
const app = new Hono();
|
95
|
+
const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
96
|
+
await pg.connect();
|
97
|
+
|
98
|
+
// That's it! Full REST API with:
|
99
|
+
// - Input validation (Zod)
|
100
|
+
// - Error handling
|
101
|
+
// - Relationship loading
|
102
|
+
// - Auth middleware (if configured)
|
103
|
+
// - Type safety throughout
|
104
|
+
|
105
|
+
const api = createRouter({ pg });
|
106
|
+
app.route("/", api);
|
107
|
+
|
108
|
+
// GET /v1/authors
|
109
|
+
// POST /v1/authors
|
110
|
+
// GET /v1/authors/:id
|
111
|
+
// PATCH /v1/authors/:id
|
112
|
+
// DELETE /v1/authors/:id
|
113
|
+
// POST /v1/authors/list (with filtering & includes)
|
114
|
+
```
|
115
|
+
|
116
|
+
### 🔒 Type Safety Everywhere
|
117
|
+
|
118
|
+
```typescript
|
119
|
+
// TypeScript catches errors at compile time
|
120
|
+
const book = await sdk.books.create({
|
121
|
+
title: "Pride and Prejudice",
|
122
|
+
author_id: "not-a-uuid", // ❌ Type error!
|
123
|
+
published_at: "invalid-date" // ❌ Type error!
|
124
|
+
});
|
125
|
+
|
126
|
+
// Generated Zod schemas for runtime validation
|
127
|
+
import { InsertBooksSchema } from "./generated/server/zod/books";
|
128
|
+
|
129
|
+
const validated = InsertBooksSchema.parse(requestBody);
|
130
|
+
```
|
131
|
+
|
132
|
+
All from your existing database schema. No manual coding required.
|
4
133
|
|
5
134
|
## Features
|
6
135
|
|
@@ -312,29 +441,285 @@ const sdk = new SDK({
|
|
312
441
|
});
|
313
442
|
```
|
314
443
|
|
315
|
-
## Server Integration
|
444
|
+
## Server Integration with Hono
|
445
|
+
|
446
|
+
The generated code integrates seamlessly with [Hono](https://hono.dev/), a lightweight web framework for the Edge.
|
447
|
+
|
448
|
+
### Basic Setup
|
316
449
|
|
317
|
-
|
450
|
+
postgresdk generates a `createRouter` function that returns a Hono router with all your routes:
|
318
451
|
|
319
452
|
```typescript
|
320
453
|
import { Hono } from "hono";
|
321
454
|
import { serve } from "@hono/node-server";
|
322
455
|
import { Client } from "pg";
|
456
|
+
import { createRouter } from "./generated/server";
|
457
|
+
|
458
|
+
const app = new Hono();
|
459
|
+
|
460
|
+
// Database connection
|
461
|
+
const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
462
|
+
await pg.connect();
|
463
|
+
|
464
|
+
// Mount all generated routes at once
|
465
|
+
const apiRouter = createRouter({ pg });
|
466
|
+
app.route("/", apiRouter);
|
467
|
+
|
468
|
+
// Start server
|
469
|
+
serve({ fetch: app.fetch, port: 3000 });
|
470
|
+
console.log("Server running on http://localhost:3000");
|
471
|
+
```
|
472
|
+
|
473
|
+
### Mounting Routes at Different Paths
|
474
|
+
|
475
|
+
The `createRouter` function returns a Hono router that can be mounted anywhere:
|
476
|
+
|
477
|
+
```typescript
|
478
|
+
import { Hono } from "hono";
|
479
|
+
import { createRouter } from "./generated/server";
|
480
|
+
|
481
|
+
const app = new Hono();
|
482
|
+
|
483
|
+
// Your existing routes
|
484
|
+
app.get("/", (c) => c.json({ message: "Welcome" }));
|
485
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
486
|
+
|
487
|
+
// Mount generated routes under /api
|
488
|
+
const apiRouter = createRouter({ pg });
|
489
|
+
app.route("/api", apiRouter); // Routes will be at /api/v1/users, /api/v1/posts, etc.
|
490
|
+
|
491
|
+
// Or mount under different version
|
492
|
+
app.route("/v2", apiRouter); // Routes will be at /v2/v1/users, /v2/v1/posts, etc.
|
493
|
+
```
|
494
|
+
|
495
|
+
### Alternative: Register Routes Directly
|
496
|
+
|
497
|
+
If you prefer to register routes directly on your app without a sub-router:
|
498
|
+
|
499
|
+
```typescript
|
500
|
+
import { registerAllRoutes } from "./generated/server";
|
501
|
+
|
502
|
+
const app = new Hono();
|
503
|
+
const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
504
|
+
await pg.connect();
|
505
|
+
|
506
|
+
// Register all routes directly on app
|
507
|
+
registerAllRoutes(app, { pg });
|
508
|
+
```
|
323
509
|
|
324
|
-
|
510
|
+
### Selective Route Registration
|
511
|
+
|
512
|
+
You can also import and register individual routes:
|
513
|
+
|
514
|
+
```typescript
|
515
|
+
import { registerUsersRoutes, registerPostsRoutes } from "./generated/server";
|
516
|
+
|
517
|
+
const app = new Hono();
|
518
|
+
|
519
|
+
// Only register specific routes
|
520
|
+
registerUsersRoutes(app, { pg });
|
521
|
+
registerPostsRoutes(app, { pg });
|
522
|
+
|
523
|
+
### Adding to an Existing Hono App
|
524
|
+
|
525
|
+
```typescript
|
526
|
+
import { Hono } from "hono";
|
527
|
+
import { cors } from "hono/cors";
|
528
|
+
import { logger } from "hono/logger";
|
529
|
+
|
530
|
+
// Your existing Hono app
|
531
|
+
const app = new Hono();
|
532
|
+
|
533
|
+
// Your existing middleware
|
534
|
+
app.use("*", cors());
|
535
|
+
app.use("*", logger());
|
536
|
+
|
537
|
+
// Your existing routes
|
538
|
+
app.get("/", (c) => c.json({ message: "Hello World" }));
|
539
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
540
|
+
|
541
|
+
// Add postgresdk generated routes
|
542
|
+
const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
543
|
+
await pg.connect();
|
544
|
+
|
545
|
+
// All generated routes are prefixed with /v1 by default
|
325
546
|
import { registerUsersRoutes } from "./generated/server/routes/users";
|
326
547
|
import { registerPostsRoutes } from "./generated/server/routes/posts";
|
327
548
|
|
549
|
+
registerUsersRoutes(app, { pg }); // Adds /v1/users/*
|
550
|
+
registerPostsRoutes(app, { pg }); // Adds /v1/posts/*
|
551
|
+
|
552
|
+
// Your routes continue to work alongside generated ones
|
553
|
+
app.get("/custom", (c) => c.json({ custom: true }));
|
554
|
+
```
|
555
|
+
|
556
|
+
### With Error Handling & Logging
|
557
|
+
|
558
|
+
```typescript
|
559
|
+
import { Hono } from "hono";
|
560
|
+
import { HTTPException } from "hono/http-exception";
|
561
|
+
|
328
562
|
const app = new Hono();
|
563
|
+
|
564
|
+
// Global error handling
|
565
|
+
app.onError((err, c) => {
|
566
|
+
if (err instanceof HTTPException) {
|
567
|
+
return err.getResponse();
|
568
|
+
}
|
569
|
+
console.error("Unhandled error:", err);
|
570
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
571
|
+
});
|
572
|
+
|
573
|
+
// Request logging middleware
|
574
|
+
app.use("*", async (c, next) => {
|
575
|
+
const start = Date.now();
|
576
|
+
await next();
|
577
|
+
const ms = Date.now() - start;
|
578
|
+
console.log(`${c.req.method} ${c.req.path} - ${c.res.status} ${ms}ms`);
|
579
|
+
});
|
580
|
+
|
581
|
+
// Register generated routes with database
|
329
582
|
const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
330
583
|
await pg.connect();
|
331
584
|
|
332
|
-
|
585
|
+
import { registerUsersRoutes } from "./generated/server/routes/users";
|
333
586
|
registerUsersRoutes(app, { pg });
|
334
|
-
|
587
|
+
```
|
335
588
|
|
336
|
-
|
589
|
+
### With Database Connection Pooling
|
590
|
+
|
591
|
+
For production, use connection pooling:
|
592
|
+
|
593
|
+
```typescript
|
594
|
+
import { Pool } from "pg";
|
595
|
+
import { Hono } from "hono";
|
596
|
+
|
597
|
+
// Use a connection pool instead of a single client
|
598
|
+
const pool = new Pool({
|
599
|
+
connectionString: process.env.DATABASE_URL,
|
600
|
+
max: 20, // Maximum number of clients in the pool
|
601
|
+
idleTimeoutMillis: 30000,
|
602
|
+
connectionTimeoutMillis: 2000,
|
603
|
+
});
|
604
|
+
|
605
|
+
const app = new Hono();
|
606
|
+
|
607
|
+
// The generated routes work with both Client and Pool
|
608
|
+
import { registerUsersRoutes } from "./generated/server/routes/users";
|
609
|
+
import { registerPostsRoutes } from "./generated/server/routes/posts";
|
610
|
+
|
611
|
+
registerUsersRoutes(app, { pg: pool });
|
612
|
+
registerPostsRoutes(app, { pg: pool });
|
613
|
+
|
614
|
+
// Graceful shutdown
|
615
|
+
process.on("SIGTERM", async () => {
|
616
|
+
await pool.end();
|
617
|
+
process.exit(0);
|
618
|
+
});
|
619
|
+
```
|
620
|
+
|
621
|
+
### With Different Deployment Targets
|
622
|
+
|
623
|
+
```typescript
|
624
|
+
// For Node.js
|
625
|
+
import { serve } from "@hono/node-server";
|
337
626
|
serve({ fetch: app.fetch, port: 3000 });
|
627
|
+
|
628
|
+
// For Cloudflare Workers
|
629
|
+
export default app;
|
630
|
+
|
631
|
+
// For Vercel
|
632
|
+
import { handle } from "@hono/vercel";
|
633
|
+
export default handle(app);
|
634
|
+
|
635
|
+
// For AWS Lambda
|
636
|
+
import { handle } from "@hono/aws-lambda";
|
637
|
+
export const handler = handle(app);
|
638
|
+
|
639
|
+
// For Deno
|
640
|
+
Deno.serve(app.fetch);
|
641
|
+
|
642
|
+
// For Bun
|
643
|
+
export default {
|
644
|
+
port: 3000,
|
645
|
+
fetch: app.fetch,
|
646
|
+
};
|
647
|
+
```
|
648
|
+
|
649
|
+
### Complete Production Example
|
650
|
+
|
651
|
+
```typescript
|
652
|
+
import { Hono } from "hono";
|
653
|
+
import { cors } from "hono/cors";
|
654
|
+
import { compress } from "hono/compress";
|
655
|
+
import { secureHeaders } from "hono/secure-headers";
|
656
|
+
import { serve } from "@hono/node-server";
|
657
|
+
import { Pool } from "pg";
|
658
|
+
|
659
|
+
// Import all generated route registrations
|
660
|
+
import { registerUsersRoutes } from "./generated/server/routes/users";
|
661
|
+
import { registerPostsRoutes } from "./generated/server/routes/posts";
|
662
|
+
import { registerCommentsRoutes } from "./generated/server/routes/comments";
|
663
|
+
|
664
|
+
// Create app with type safety
|
665
|
+
const app = new Hono();
|
666
|
+
|
667
|
+
// Production middleware stack
|
668
|
+
app.use("*", cors({
|
669
|
+
origin: process.env.ALLOWED_ORIGINS?.split(",") || "*",
|
670
|
+
credentials: true,
|
671
|
+
}));
|
672
|
+
app.use("*", compress());
|
673
|
+
app.use("*", secureHeaders());
|
674
|
+
|
675
|
+
// Health check
|
676
|
+
app.get("/health", (c) => c.json({
|
677
|
+
status: "ok",
|
678
|
+
timestamp: new Date().toISOString()
|
679
|
+
}));
|
680
|
+
|
681
|
+
// Database connection pool
|
682
|
+
const pool = new Pool({
|
683
|
+
connectionString: process.env.DATABASE_URL,
|
684
|
+
ssl: process.env.NODE_ENV === "production" ? { rejectUnauthorized: false } : false,
|
685
|
+
max: 20,
|
686
|
+
});
|
687
|
+
|
688
|
+
// Register all generated routes
|
689
|
+
registerUsersRoutes(app, { pg: pool });
|
690
|
+
registerPostsRoutes(app, { pg: pool });
|
691
|
+
registerCommentsRoutes(app, { pg: pool });
|
692
|
+
|
693
|
+
// 404 handler
|
694
|
+
app.notFound((c) => c.json({ error: "Not Found" }, 404));
|
695
|
+
|
696
|
+
// Global error handler
|
697
|
+
app.onError((err, c) => {
|
698
|
+
console.error(`Error ${c.req.method} ${c.req.path}:`, err);
|
699
|
+
return c.json({
|
700
|
+
error: process.env.NODE_ENV === "production"
|
701
|
+
? "Internal Server Error"
|
702
|
+
: err.message
|
703
|
+
}, 500);
|
704
|
+
});
|
705
|
+
|
706
|
+
// Start server
|
707
|
+
const port = parseInt(process.env.PORT || "3000");
|
708
|
+
serve({
|
709
|
+
fetch: app.fetch,
|
710
|
+
port,
|
711
|
+
hostname: "0.0.0.0"
|
712
|
+
});
|
713
|
+
|
714
|
+
console.log(`Server running on http://localhost:${port}`);
|
715
|
+
console.log(`Environment: ${process.env.NODE_ENV || "development"}`);
|
716
|
+
|
717
|
+
// Graceful shutdown
|
718
|
+
process.on("SIGTERM", async () => {
|
719
|
+
console.log("SIGTERM received, closing connections...");
|
720
|
+
await pool.end();
|
721
|
+
process.exit(0);
|
722
|
+
});
|
338
723
|
```
|
339
724
|
|
340
725
|
## CLI Options
|
package/dist/cli.js
CHANGED
@@ -1198,6 +1198,87 @@ export async function authMiddleware(c: Context, next: Next) {
|
|
1198
1198
|
`;
|
1199
1199
|
}
|
1200
1200
|
|
1201
|
+
// src/emit-server-index.ts
|
1202
|
+
function emitServerIndex(tables, hasAuth) {
|
1203
|
+
const tableNames = tables.map((t) => t.name).sort();
|
1204
|
+
const imports = tableNames.map((name) => {
|
1205
|
+
const Type = pascal(name);
|
1206
|
+
return `import { register${Type}Routes } from "./routes/${name}";`;
|
1207
|
+
}).join(`
|
1208
|
+
`);
|
1209
|
+
const registrations = tableNames.map((name) => {
|
1210
|
+
const Type = pascal(name);
|
1211
|
+
return ` register${Type}Routes(router, deps);`;
|
1212
|
+
}).join(`
|
1213
|
+
`);
|
1214
|
+
const reExports = tableNames.map((name) => {
|
1215
|
+
const Type = pascal(name);
|
1216
|
+
return `export { register${Type}Routes } from "./routes/${name}";`;
|
1217
|
+
}).join(`
|
1218
|
+
`);
|
1219
|
+
return `/* Generated. Do not edit. */
|
1220
|
+
import { Hono } from "hono";
|
1221
|
+
${imports}
|
1222
|
+
${hasAuth ? `export { authMiddleware } from "./auth";` : ""}
|
1223
|
+
|
1224
|
+
/**
|
1225
|
+
* Creates a Hono router with all generated routes that can be mounted into your existing app.
|
1226
|
+
*
|
1227
|
+
* @example
|
1228
|
+
* import { Hono } from "hono";
|
1229
|
+
* import { Client } from "pg";
|
1230
|
+
* import { createRouter } from "./generated/server";
|
1231
|
+
*
|
1232
|
+
* const app = new Hono();
|
1233
|
+
* const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
1234
|
+
* await pg.connect();
|
1235
|
+
*
|
1236
|
+
* // Mount all generated routes under /api
|
1237
|
+
* const apiRouter = createRouter({ pg });
|
1238
|
+
* app.route("/api", apiRouter);
|
1239
|
+
*
|
1240
|
+
* // Or mount directly at root
|
1241
|
+
* const router = createRouter({ pg });
|
1242
|
+
* app.route("/", router);
|
1243
|
+
*/
|
1244
|
+
export function createRouter(
|
1245
|
+
deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }
|
1246
|
+
): Hono {
|
1247
|
+
const router = new Hono();
|
1248
|
+
${registrations}
|
1249
|
+
return router;
|
1250
|
+
}
|
1251
|
+
|
1252
|
+
/**
|
1253
|
+
* Register all generated routes directly on an existing Hono app.
|
1254
|
+
*
|
1255
|
+
* @example
|
1256
|
+
* import { Hono } from "hono";
|
1257
|
+
* import { Client } from "pg";
|
1258
|
+
* import { registerAllRoutes } from "./generated/server";
|
1259
|
+
*
|
1260
|
+
* const app = new Hono();
|
1261
|
+
* const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
1262
|
+
* await pg.connect();
|
1263
|
+
*
|
1264
|
+
* // Register all routes at once
|
1265
|
+
* registerAllRoutes(app, { pg });
|
1266
|
+
*/
|
1267
|
+
export function registerAllRoutes(
|
1268
|
+
app: Hono,
|
1269
|
+
deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }
|
1270
|
+
) {
|
1271
|
+
${registrations.replace(/router/g, "app")}
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
// Individual route registrations (for selective use)
|
1275
|
+
${reExports}
|
1276
|
+
|
1277
|
+
// Re-export types and schemas for convenience
|
1278
|
+
export * from "./include-spec";
|
1279
|
+
`;
|
1280
|
+
}
|
1281
|
+
|
1201
1282
|
// src/index.ts
|
1202
1283
|
async function generate(configPath) {
|
1203
1284
|
const configUrl = pathToFileURL(configPath).href;
|
@@ -1260,6 +1341,10 @@ async function generate(configPath) {
|
|
1260
1341
|
path: join(clientDir, "index.ts"),
|
1261
1342
|
content: emitClientIndex(Object.values(model.tables))
|
1262
1343
|
});
|
1344
|
+
files.push({
|
1345
|
+
path: join(serverDir, "index.ts"),
|
1346
|
+
content: emitServerIndex(Object.values(model.tables), !!cfg.auth?.strategy && cfg.auth.strategy !== "none")
|
1347
|
+
});
|
1263
1348
|
console.log("✍️ Writing files...");
|
1264
1349
|
await writeFiles(files);
|
1265
1350
|
console.log(`✅ Generated ${files.length} files`);
|
package/dist/index.js
CHANGED
@@ -1196,6 +1196,87 @@ export async function authMiddleware(c: Context, next: Next) {
|
|
1196
1196
|
`;
|
1197
1197
|
}
|
1198
1198
|
|
1199
|
+
// src/emit-server-index.ts
|
1200
|
+
function emitServerIndex(tables, hasAuth) {
|
1201
|
+
const tableNames = tables.map((t) => t.name).sort();
|
1202
|
+
const imports = tableNames.map((name) => {
|
1203
|
+
const Type = pascal(name);
|
1204
|
+
return `import { register${Type}Routes } from "./routes/${name}";`;
|
1205
|
+
}).join(`
|
1206
|
+
`);
|
1207
|
+
const registrations = tableNames.map((name) => {
|
1208
|
+
const Type = pascal(name);
|
1209
|
+
return ` register${Type}Routes(router, deps);`;
|
1210
|
+
}).join(`
|
1211
|
+
`);
|
1212
|
+
const reExports = tableNames.map((name) => {
|
1213
|
+
const Type = pascal(name);
|
1214
|
+
return `export { register${Type}Routes } from "./routes/${name}";`;
|
1215
|
+
}).join(`
|
1216
|
+
`);
|
1217
|
+
return `/* Generated. Do not edit. */
|
1218
|
+
import { Hono } from "hono";
|
1219
|
+
${imports}
|
1220
|
+
${hasAuth ? `export { authMiddleware } from "./auth";` : ""}
|
1221
|
+
|
1222
|
+
/**
|
1223
|
+
* Creates a Hono router with all generated routes that can be mounted into your existing app.
|
1224
|
+
*
|
1225
|
+
* @example
|
1226
|
+
* import { Hono } from "hono";
|
1227
|
+
* import { Client } from "pg";
|
1228
|
+
* import { createRouter } from "./generated/server";
|
1229
|
+
*
|
1230
|
+
* const app = new Hono();
|
1231
|
+
* const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
1232
|
+
* await pg.connect();
|
1233
|
+
*
|
1234
|
+
* // Mount all generated routes under /api
|
1235
|
+
* const apiRouter = createRouter({ pg });
|
1236
|
+
* app.route("/api", apiRouter);
|
1237
|
+
*
|
1238
|
+
* // Or mount directly at root
|
1239
|
+
* const router = createRouter({ pg });
|
1240
|
+
* app.route("/", router);
|
1241
|
+
*/
|
1242
|
+
export function createRouter(
|
1243
|
+
deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }
|
1244
|
+
): Hono {
|
1245
|
+
const router = new Hono();
|
1246
|
+
${registrations}
|
1247
|
+
return router;
|
1248
|
+
}
|
1249
|
+
|
1250
|
+
/**
|
1251
|
+
* Register all generated routes directly on an existing Hono app.
|
1252
|
+
*
|
1253
|
+
* @example
|
1254
|
+
* import { Hono } from "hono";
|
1255
|
+
* import { Client } from "pg";
|
1256
|
+
* import { registerAllRoutes } from "./generated/server";
|
1257
|
+
*
|
1258
|
+
* const app = new Hono();
|
1259
|
+
* const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
1260
|
+
* await pg.connect();
|
1261
|
+
*
|
1262
|
+
* // Register all routes at once
|
1263
|
+
* registerAllRoutes(app, { pg });
|
1264
|
+
*/
|
1265
|
+
export function registerAllRoutes(
|
1266
|
+
app: Hono,
|
1267
|
+
deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }
|
1268
|
+
) {
|
1269
|
+
${registrations.replace(/router/g, "app")}
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
// Individual route registrations (for selective use)
|
1273
|
+
${reExports}
|
1274
|
+
|
1275
|
+
// Re-export types and schemas for convenience
|
1276
|
+
export * from "./include-spec";
|
1277
|
+
`;
|
1278
|
+
}
|
1279
|
+
|
1199
1280
|
// src/index.ts
|
1200
1281
|
async function generate(configPath) {
|
1201
1282
|
const configUrl = pathToFileURL(configPath).href;
|
@@ -1258,6 +1339,10 @@ async function generate(configPath) {
|
|
1258
1339
|
path: join(clientDir, "index.ts"),
|
1259
1340
|
content: emitClientIndex(Object.values(model.tables))
|
1260
1341
|
});
|
1342
|
+
files.push({
|
1343
|
+
path: join(serverDir, "index.ts"),
|
1344
|
+
content: emitServerIndex(Object.values(model.tables), !!cfg.auth?.strategy && cfg.auth.strategy !== "none")
|
1345
|
+
});
|
1261
1346
|
console.log("✍️ Writing files...");
|
1262
1347
|
await writeFiles(files);
|
1263
1348
|
console.log(`✅ Generated ${files.length} files`);
|