postgresdk 0.1.1-alpha.0 → 0.1.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/README.md +404 -5
- package/dist/cli.js +252 -4777
- package/dist/emit-auth.d.ts +2 -0
- package/dist/emit-routes.d.ts +1 -0
- package/dist/index.js +248 -4772
- package/dist/types.d.ts +20 -0
- package/package.json +5 -4
package/README.md
CHANGED
@@ -1,15 +1,414 @@
|
|
1
|
-
#
|
1
|
+
# postgresdk
|
2
2
|
|
3
|
-
|
3
|
+
Generate a fully-typed, production-ready SDK from your PostgreSQL database schema. Automatically creates both server-side REST API routes and client-side SDK with TypeScript types, Zod validation, and support for complex relationships.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 🚀 **Instant SDK Generation** - Point at your PostgreSQL database and get a complete SDK
|
8
|
+
- 🔒 **Type Safety** - Full TypeScript types derived from your database schema
|
9
|
+
- ✅ **Runtime Validation** - Zod schemas for request/response validation
|
10
|
+
- 🔗 **Smart Relationships** - Automatic handling of 1:N and M:N relationships with eager loading
|
11
|
+
- 🔐 **Built-in Auth** - API key and JWT authentication with zero configuration
|
12
|
+
- 🎯 **Zero Config** - Works out of the box with sensible defaults
|
13
|
+
- 🏗️ **Framework Ready** - Server routes built for Hono, client SDK works anywhere
|
14
|
+
- 📦 **Lightweight** - Minimal dependencies, optimized for production
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
```bash
|
19
|
+
npm install -g postgresdk
|
20
|
+
# or
|
21
|
+
npx postgresdk
|
22
|
+
```
|
23
|
+
|
24
|
+
## Quick Start
|
25
|
+
|
26
|
+
1. Create a configuration file `postgresdk.config.ts`:
|
27
|
+
|
28
|
+
```typescript
|
29
|
+
export default {
|
30
|
+
connectionString: "postgres://user:pass@localhost:5432/mydb",
|
31
|
+
schema: "public",
|
32
|
+
outServer: "./generated/server",
|
33
|
+
outClient: "./generated/client",
|
34
|
+
};
|
35
|
+
```
|
36
|
+
|
37
|
+
2. Run the generator:
|
4
38
|
|
5
39
|
```bash
|
40
|
+
postgresdk
|
41
|
+
```
|
42
|
+
|
43
|
+
3. Use the generated SDK:
|
44
|
+
|
45
|
+
```typescript
|
46
|
+
// Server (Hono)
|
47
|
+
import { Hono } from "hono";
|
48
|
+
import { Client } from "pg";
|
49
|
+
import { registerUsersRoutes } from "./generated/server/routes/users";
|
50
|
+
|
51
|
+
const app = new Hono();
|
52
|
+
const pg = new Client({ connectionString: "..." });
|
53
|
+
await pg.connect();
|
54
|
+
|
55
|
+
registerUsersRoutes(app, { pg });
|
56
|
+
|
57
|
+
// Client
|
58
|
+
import { SDK } from "./generated/client";
|
59
|
+
|
60
|
+
const sdk = new SDK({ baseUrl: "http://localhost:3000" });
|
61
|
+
|
62
|
+
// Full CRUD operations with TypeScript types
|
63
|
+
const user = await sdk.users.create({ name: "Alice", email: "alice@example.com" });
|
64
|
+
const users = await sdk.users.list({ include: { posts: true } });
|
65
|
+
await sdk.users.update(user.id, { name: "Alice Smith" });
|
66
|
+
await sdk.users.delete(user.id);
|
67
|
+
```
|
68
|
+
|
69
|
+
## Configuration
|
70
|
+
|
71
|
+
Create a `postgresdk.config.ts` file in your project root:
|
72
|
+
|
73
|
+
```typescript
|
74
|
+
export default {
|
75
|
+
// Required
|
76
|
+
connectionString: process.env.DATABASE_URL || "postgres://user:pass@localhost:5432/dbname",
|
77
|
+
|
78
|
+
// Optional (with defaults)
|
79
|
+
schema: "public", // Database schema to introspect
|
80
|
+
outServer: "./generated/server", // Server code output directory
|
81
|
+
outClient: "./generated/client", // Client SDK output directory
|
82
|
+
softDeleteColumn: null, // Column name for soft deletes (e.g., "deleted_at")
|
83
|
+
includeDepthLimit: 3, // Max depth for nested includes
|
84
|
+
dateType: "date", // "date" | "string" - How to handle timestamps
|
85
|
+
|
86
|
+
// Authentication (optional)
|
87
|
+
auth: {
|
88
|
+
strategy: "none" | "api-key" | "jwt-hs256", // Default: "none"
|
89
|
+
|
90
|
+
// For API key auth
|
91
|
+
apiKeyHeader: "x-api-key", // Header name for API key
|
92
|
+
apiKeys: ["key1", "key2"], // Array of valid keys
|
93
|
+
|
94
|
+
// For JWT auth (HS256)
|
95
|
+
jwt: {
|
96
|
+
sharedSecret: "your-secret", // Shared secret for HS256
|
97
|
+
issuer: "your-app", // Optional: validate issuer claim
|
98
|
+
audience: "your-audience" // Optional: validate audience claim
|
99
|
+
}
|
100
|
+
}
|
101
|
+
};
|
102
|
+
```
|
103
|
+
|
104
|
+
Environment variables work directly in the config file - no function wrapper needed.
|
105
|
+
|
106
|
+
## Generated SDK Features
|
107
|
+
|
108
|
+
### CRUD Operations
|
109
|
+
|
110
|
+
Every table gets a complete set of CRUD operations:
|
111
|
+
|
112
|
+
```typescript
|
113
|
+
// Create
|
114
|
+
const user = await sdk.users.create({ name: "Bob", email: "bob@example.com" });
|
115
|
+
|
116
|
+
// Read
|
117
|
+
const user = await sdk.users.getByPk(123);
|
118
|
+
const users = await sdk.users.list();
|
119
|
+
|
120
|
+
// Update
|
121
|
+
const updated = await sdk.users.update(123, { name: "Robert" });
|
122
|
+
|
123
|
+
// Delete
|
124
|
+
const deleted = await sdk.users.delete(123);
|
125
|
+
```
|
126
|
+
|
127
|
+
### Relationships & Eager Loading
|
128
|
+
|
129
|
+
Automatically handles relationships with the `include` parameter:
|
130
|
+
|
131
|
+
```typescript
|
132
|
+
// 1:N relationship - Get authors with their books
|
133
|
+
const authors = await sdk.authors.list({
|
134
|
+
include: { books: true }
|
135
|
+
});
|
136
|
+
|
137
|
+
// M:N relationship - Get books with their tags
|
138
|
+
const books = await sdk.books.list({
|
139
|
+
include: { tags: true }
|
140
|
+
});
|
141
|
+
|
142
|
+
// Nested includes - Get authors with books and their tags
|
143
|
+
const authors = await sdk.authors.list({
|
144
|
+
include: {
|
145
|
+
books: {
|
146
|
+
include: {
|
147
|
+
tags: true
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
});
|
152
|
+
```
|
153
|
+
|
154
|
+
### Filtering & Pagination
|
155
|
+
|
156
|
+
```typescript
|
157
|
+
const users = await sdk.users.list({
|
158
|
+
where: { status: "active" },
|
159
|
+
orderBy: "created_at",
|
160
|
+
order: "desc",
|
161
|
+
limit: 20,
|
162
|
+
offset: 40
|
163
|
+
});
|
164
|
+
```
|
165
|
+
|
166
|
+
### Type Safety
|
167
|
+
|
168
|
+
All operations are fully typed based on your database schema:
|
169
|
+
|
170
|
+
```typescript
|
171
|
+
// TypeScript will enforce correct types
|
172
|
+
const user = await sdk.users.create({
|
173
|
+
name: "Alice", // ✅ string required
|
174
|
+
email: "alice@...", // ✅ string required
|
175
|
+
age: "30" // ❌ Type error: should be number
|
176
|
+
});
|
177
|
+
```
|
178
|
+
|
179
|
+
## Authentication
|
180
|
+
|
181
|
+
postgresdk supports three authentication strategies out of the box:
|
182
|
+
|
183
|
+
### No Authentication (Default)
|
184
|
+
|
185
|
+
```typescript
|
186
|
+
// postgresdk.config.ts
|
187
|
+
export default {
|
188
|
+
connectionString: "...",
|
189
|
+
// No auth config needed - routes are unprotected
|
190
|
+
};
|
191
|
+
```
|
192
|
+
|
193
|
+
### API Key Authentication
|
194
|
+
|
195
|
+
```typescript
|
196
|
+
// postgresdk.config.ts
|
197
|
+
export default {
|
198
|
+
connectionString: "...",
|
199
|
+
auth: {
|
200
|
+
strategy: "api-key",
|
201
|
+
apiKeyHeader: "x-api-key", // Optional, defaults to "x-api-key"
|
202
|
+
apiKeys: [
|
203
|
+
"your-api-key-1",
|
204
|
+
"your-api-key-2",
|
205
|
+
// Can also use environment variables
|
206
|
+
"env:API_KEYS" // Reads comma-separated keys from process.env.API_KEYS
|
207
|
+
]
|
208
|
+
}
|
209
|
+
};
|
210
|
+
|
211
|
+
// Client SDK usage
|
212
|
+
const sdk = new SDK({
|
213
|
+
baseUrl: "http://localhost:3000",
|
214
|
+
auth: { apiKey: "your-api-key-1" }
|
215
|
+
});
|
216
|
+
```
|
217
|
+
|
218
|
+
### JWT Authentication (HS256)
|
219
|
+
|
220
|
+
```typescript
|
221
|
+
// postgresdk.config.ts
|
222
|
+
export default {
|
223
|
+
connectionString: "...",
|
224
|
+
auth: {
|
225
|
+
strategy: "jwt-hs256",
|
226
|
+
jwt: {
|
227
|
+
sharedSecret: process.env.JWT_SECRET || "your-secret-key",
|
228
|
+
issuer: "my-app", // Optional: validates 'iss' claim
|
229
|
+
audience: "my-users" // Optional: validates 'aud' claim
|
230
|
+
}
|
231
|
+
}
|
232
|
+
};
|
233
|
+
|
234
|
+
// Client SDK usage with static token
|
235
|
+
const sdk = new SDK({
|
236
|
+
baseUrl: "http://localhost:3000",
|
237
|
+
auth: { jwt: "eyJhbGciOiJIUzI1NiIs..." }
|
238
|
+
});
|
239
|
+
|
240
|
+
// Or with dynamic token provider
|
241
|
+
const sdk = new SDK({
|
242
|
+
baseUrl: "http://localhost:3000",
|
243
|
+
auth: {
|
244
|
+
jwt: async () => {
|
245
|
+
// Refresh token if needed
|
246
|
+
return await getAccessToken();
|
247
|
+
}
|
248
|
+
}
|
249
|
+
});
|
250
|
+
|
251
|
+
// Or with custom auth headers
|
252
|
+
const sdk = new SDK({
|
253
|
+
baseUrl: "http://localhost:3000",
|
254
|
+
auth: async () => ({
|
255
|
+
"Authorization": `Bearer ${await getToken()}`,
|
256
|
+
"X-Tenant-ID": "tenant-123"
|
257
|
+
})
|
258
|
+
});
|
259
|
+
```
|
260
|
+
|
261
|
+
### Environment Variables in Auth Config
|
262
|
+
|
263
|
+
The auth configuration supports environment variables with the `env:` prefix:
|
264
|
+
|
265
|
+
```typescript
|
266
|
+
export default {
|
267
|
+
auth: {
|
268
|
+
strategy: "api-key",
|
269
|
+
apiKeys: ["env:API_KEYS"], // Reads from process.env.API_KEYS
|
270
|
+
|
271
|
+
// Or for JWT
|
272
|
+
strategy: "jwt-hs256",
|
273
|
+
jwt: {
|
274
|
+
sharedSecret: "env:JWT_SECRET" // Reads from process.env.JWT_SECRET
|
275
|
+
}
|
276
|
+
}
|
277
|
+
};
|
278
|
+
```
|
279
|
+
|
280
|
+
### How Auth Works
|
281
|
+
|
282
|
+
When authentication is configured:
|
283
|
+
|
284
|
+
1. **Server Side**: All generated routes are automatically protected with the configured auth middleware
|
285
|
+
2. **Client Side**: The SDK handles auth headers transparently
|
286
|
+
3. **Type Safety**: Auth configuration is fully typed
|
287
|
+
4. **Zero Overhead**: When `strategy: "none"`, no auth code is included
|
288
|
+
|
289
|
+
### JWT Token Generation Example
|
290
|
+
|
291
|
+
```typescript
|
292
|
+
// Install jose for JWT generation: npm install jose
|
293
|
+
import { SignJWT } from 'jose';
|
294
|
+
|
295
|
+
const secret = new TextEncoder().encode('your-secret-key');
|
296
|
+
|
297
|
+
const token = await new SignJWT({
|
298
|
+
sub: 'user123',
|
299
|
+
email: 'user@example.com',
|
300
|
+
roles: ['admin']
|
301
|
+
})
|
302
|
+
.setProtectedHeader({ alg: 'HS256' })
|
303
|
+
.setIssuer('my-app')
|
304
|
+
.setAudience('my-users')
|
305
|
+
.setExpirationTime('2h')
|
306
|
+
.sign(secret);
|
307
|
+
|
308
|
+
// Use with SDK
|
309
|
+
const sdk = new SDK({
|
310
|
+
baseUrl: "http://localhost:3000",
|
311
|
+
auth: { jwt: token }
|
312
|
+
});
|
313
|
+
```
|
314
|
+
|
315
|
+
## Server Integration
|
316
|
+
|
317
|
+
The generated server code is designed for [Hono](https://hono.dev/) but can be adapted to other frameworks:
|
318
|
+
|
319
|
+
```typescript
|
320
|
+
import { Hono } from "hono";
|
321
|
+
import { serve } from "@hono/node-server";
|
322
|
+
import { Client } from "pg";
|
323
|
+
|
324
|
+
// Import generated route registrations
|
325
|
+
import { registerUsersRoutes } from "./generated/server/routes/users";
|
326
|
+
import { registerPostsRoutes } from "./generated/server/routes/posts";
|
327
|
+
|
328
|
+
const app = new Hono();
|
329
|
+
const pg = new Client({ connectionString: process.env.DATABASE_URL });
|
330
|
+
await pg.connect();
|
331
|
+
|
332
|
+
// Register routes
|
333
|
+
registerUsersRoutes(app, { pg });
|
334
|
+
registerPostsRoutes(app, { pg });
|
335
|
+
|
336
|
+
// Start server
|
337
|
+
serve({ fetch: app.fetch, port: 3000 });
|
338
|
+
```
|
339
|
+
|
340
|
+
## CLI Options
|
341
|
+
|
342
|
+
```bash
|
343
|
+
postgresdk [options]
|
344
|
+
|
345
|
+
Options:
|
346
|
+
-c, --config <path> Path to config file (default: postgresdk.config.ts)
|
347
|
+
-v, --version Show version
|
348
|
+
-h, --help Show help
|
349
|
+
```
|
350
|
+
|
351
|
+
## How It Works
|
352
|
+
|
353
|
+
1. **Introspection** - Connects to your PostgreSQL database and reads the schema
|
354
|
+
2. **Relationship Detection** - Analyzes foreign keys to understand table relationships
|
355
|
+
3. **Code Generation** - Generates TypeScript code for:
|
356
|
+
- Type definitions from table schemas
|
357
|
+
- Zod validation schemas
|
358
|
+
- REST API route handlers
|
359
|
+
- Client SDK with full typing
|
360
|
+
- Include/eager-loading system
|
361
|
+
4. **Output** - Writes generated files to specified directories
|
362
|
+
|
363
|
+
## Requirements
|
364
|
+
|
365
|
+
- Node.js 20+
|
366
|
+
- PostgreSQL 12+
|
367
|
+
- TypeScript project (for using generated code)
|
368
|
+
- Optional: `jose` package for JWT authentication (auto-installed when using JWT auth)
|
369
|
+
|
370
|
+
## Development
|
371
|
+
|
372
|
+
```bash
|
373
|
+
# Clone the repository
|
374
|
+
git clone https://github.com/adpharm/postgresdk.git
|
375
|
+
cd postgresdk
|
376
|
+
|
377
|
+
# Install dependencies
|
6
378
|
bun install
|
379
|
+
|
380
|
+
# Run tests (starts PostgreSQL in Docker)
|
381
|
+
bun test
|
382
|
+
|
383
|
+
# Build
|
384
|
+
bun run build
|
385
|
+
|
386
|
+
# Publish new version
|
387
|
+
./publish.sh
|
7
388
|
```
|
8
389
|
|
9
|
-
|
390
|
+
## Testing
|
391
|
+
|
392
|
+
The test suite automatically manages a PostgreSQL Docker container:
|
10
393
|
|
11
394
|
```bash
|
12
|
-
bun
|
395
|
+
bun test
|
13
396
|
```
|
14
397
|
|
15
|
-
|
398
|
+
Tests cover:
|
399
|
+
- CRUD operations for all entities
|
400
|
+
- 1:N and M:N relationships
|
401
|
+
- Nested includes
|
402
|
+
- Validation and error handling
|
403
|
+
|
404
|
+
## License
|
405
|
+
|
406
|
+
MIT
|
407
|
+
|
408
|
+
## Contributing
|
409
|
+
|
410
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
411
|
+
|
412
|
+
## Support
|
413
|
+
|
414
|
+
For issues and feature requests, please [create an issue](https://github.com/adpharm/postgresdk/issues).
|