@idealyst/cli 1.0.46 → 1.0.48
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/dist/generators/fullstack.js +61 -3
- package/dist/generators/fullstack.js.map +1 -1
- package/dist/generators/native.js +12 -0
- package/dist/generators/native.js.map +1 -1
- package/dist/generators/utils.js +64 -31
- package/dist/generators/utils.js.map +1 -1
- package/dist/templates/api/README.md +207 -130
- package/dist/templates/api/package.json +5 -5
- package/dist/templates/api/src/controllers/TestController.ts +0 -0
- package/dist/templates/api/src/index.ts +2 -7
- package/dist/templates/api/src/lib/crud.ts +150 -0
- package/dist/templates/api/src/lib/database.ts +23 -0
- package/dist/templates/api/src/router/index.ts +104 -71
- package/dist/templates/api/src/routers/test.ts +59 -0
- package/dist/templates/api/src/routers/user.example.ts +83 -0
- package/dist/templates/api/src/server.ts +1 -1
- package/dist/templates/api/tsconfig.json +0 -1
- package/dist/templates/database/README.md +115 -1
- package/dist/templates/database/package.json +2 -0
- package/dist/templates/database/prisma/seed.ts +37 -1
- package/dist/templates/database/schema.prisma +11 -1
- package/dist/templates/native/index.js +1 -1
- package/dist/templates/native/metro.config.js +1 -1
- package/dist/templates/native/package.json +4 -0
- package/dist/templates/native/src/App.tsx +16 -0
- package/dist/templates/native/src/utils/trpc.ts +7 -127
- package/dist/templates/native/tsconfig.json +0 -2
- package/dist/templates/shared/package.json +8 -3
- package/dist/templates/shared/src/components/App.tsx +57 -0
- package/dist/templates/shared/src/components/HelloWorld.tsx +276 -86
- package/dist/templates/shared/src/components/index.ts +1 -1
- package/dist/templates/shared/src/index.ts +6 -2
- package/dist/templates/shared/src/trpc/client.ts +39 -0
- package/dist/templates/shared/tsconfig.json +1 -1
- package/dist/templates/web/README.md +65 -8
- package/dist/templates/web/package.json +3 -3
- package/dist/templates/web/src/App-with-trpc-and-shared.tsx +10 -6
- package/dist/templates/web/src/components/TestDemo.tsx +164 -0
- package/dist/templates/web/src/utils/trpc.ts +7 -93
- package/dist/templates/web/tsconfig.json +0 -1
- package/dist/templates/workspace/.devcontainer/devcontainer.json +4 -9
- package/dist/templates/workspace/.devcontainer/docker-compose.yml +1 -2
- package/dist/templates/workspace/.devcontainer/setup.sh +1 -1
- package/dist/templates/workspace/.env.example +1 -1
- package/dist/templates/workspace/Dockerfile +4 -4
- package/dist/templates/workspace/docker/nginx/prod.conf +2 -2
- package/dist/templates/workspace/docker/nginx.conf +1 -1
- package/dist/templates/workspace/docker/prometheus/prometheus.yml +1 -1
- package/dist/templates/workspace/docker-compose.yml +4 -5
- package/dist/templates/workspace/tsconfig.json +0 -1
- package/package.json +1 -1
- package/templates/api/README.md +207 -130
- package/templates/api/package.json +5 -5
- package/templates/api/src/controllers/TestController.ts +0 -0
- package/templates/api/src/index.ts +2 -7
- package/templates/api/src/lib/crud.ts +150 -0
- package/templates/api/src/lib/database.ts +23 -0
- package/templates/api/src/router/index.ts +104 -71
- package/templates/api/src/routers/test.ts +59 -0
- package/templates/api/src/routers/user.example.ts +83 -0
- package/templates/api/src/server.ts +1 -1
- package/templates/api/tsconfig.json +0 -1
- package/templates/database/README.md +115 -1
- package/templates/database/package.json +2 -0
- package/templates/database/prisma/seed.ts +37 -1
- package/templates/database/schema.prisma +11 -1
- package/templates/native/index.js +1 -1
- package/templates/native/metro.config.js +1 -1
- package/templates/native/package.json +4 -0
- package/templates/native/src/App.tsx +16 -0
- package/templates/native/src/utils/trpc.ts +7 -127
- package/templates/native/tsconfig.json +0 -2
- package/templates/shared/package.json +8 -3
- package/templates/shared/src/components/App.tsx +57 -0
- package/templates/shared/src/components/HelloWorld.tsx +276 -86
- package/templates/shared/src/components/index.ts +1 -1
- package/templates/shared/src/index.ts +6 -2
- package/templates/shared/src/trpc/client.ts +39 -0
- package/templates/shared/tsconfig.json +1 -1
- package/templates/web/README.md +65 -8
- package/templates/web/package.json +3 -3
- package/templates/web/src/App-with-trpc-and-shared.tsx +10 -6
- package/templates/web/src/components/TestDemo.tsx +164 -0
- package/templates/web/src/utils/trpc.ts +7 -93
- package/templates/web/tsconfig.json +0 -1
- package/templates/workspace/.devcontainer/devcontainer.json +4 -9
- package/templates/workspace/.devcontainer/docker-compose.yml +1 -2
- package/templates/workspace/.devcontainer/setup.sh +1 -1
- package/templates/workspace/.env.example +1 -1
- package/templates/workspace/Dockerfile +4 -4
- package/templates/workspace/docker/nginx/prod.conf +2 -2
- package/templates/workspace/docker/nginx.conf +1 -1
- package/templates/workspace/docker/prometheus/prometheus.yml +1 -1
- package/templates/workspace/docker-compose.yml +4 -5
- package/templates/workspace/tsconfig.json +0 -1
- package/dist/templates/api/src/controllers/UserController.ts +0 -102
- package/dist/templates/api/src/lib/controller.ts +0 -90
- package/dist/templates/api/src/lib/middleware.ts +0 -170
- package/dist/templates/api/src/middleware/auth.ts +0 -75
- package/dist/templates/api/src/middleware/common.ts +0 -103
- package/dist/templates/database/.env.example +0 -1
- package/dist/templates/native/App.tsx +0 -23
- package/dist/templates/native/src/App-with-trpc-and-shared.tsx +0 -12
- package/templates/api/src/controllers/UserController.ts +0 -102
- package/templates/api/src/lib/controller.ts +0 -90
- package/templates/api/src/lib/middleware.ts +0 -170
- package/templates/api/src/middleware/auth.ts +0 -75
- package/templates/api/src/middleware/common.ts +0 -103
- package/templates/database/.env.example +0 -1
- package/templates/native/App.tsx +0 -23
- package/templates/native/src/App-with-trpc-and-shared.tsx +0 -12
package/templates/api/README.md
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
|
-
# {{projectName}}
|
|
1
|
+
# {{projectName}} API
|
|
2
2
|
|
|
3
3
|
{{description}}
|
|
4
4
|
|
|
5
|
+
A simplified tRPC API with automatic CRUD generation for Prisma models.
|
|
6
|
+
|
|
5
7
|
This API project is built with:
|
|
6
|
-
- **tRPC** - End-to-end
|
|
7
|
-
|
|
8
|
+
- **tRPC** - End-to-end```typescript
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { router, publicProcedure } from '../trpc.js';
|
|
11
|
+
import { createCrudRouter } from '../lib/crud.js';
|
|
12
|
+
import { prisma } from '../lib/database.js';
|
|
13
|
+
|
|
14
|
+
const baseCrudRouter = createCrudRouter('user', createUserSchema);
|
|
15
|
+
|
|
16
|
+
export const userRouter = router({
|
|
17
|
+
...baseCrudRouter,
|
|
18
|
+
|
|
19
|
+
// Add custom procedures
|
|
20
|
+
getByEmail: publicProcedure
|
|
21
|
+
.input(z.object({ email: z.string().email() }))
|
|
22
|
+
.query(async ({ input }) => {
|
|
23
|
+
return await prisma.user.findUnique({
|
|
24
|
+
where: { email: input.email }
|
|
25
|
+
});
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
```Zod** - TypeScript-first schema validation
|
|
8
29
|
- **Express.js** - Web framework for Node.js
|
|
9
30
|
- **TypeScript** - Type-safe JavaScript
|
|
31
|
+
- **Prisma** - Next-generation ORM for database access
|
|
10
32
|
|
|
11
33
|
## Quick Start
|
|
12
34
|
|
|
@@ -20,178 +42,233 @@ This API project is built with:
|
|
|
20
42
|
yarn install
|
|
21
43
|
```
|
|
22
44
|
|
|
23
|
-
3. **
|
|
45
|
+
3. **Setup database:**
|
|
46
|
+
```bash
|
|
47
|
+
# Generate Prisma client
|
|
48
|
+
yarn prisma:generate
|
|
49
|
+
|
|
50
|
+
# Run migrations
|
|
51
|
+
yarn prisma:migrate
|
|
52
|
+
|
|
53
|
+
# Seed database with sample data
|
|
54
|
+
yarn prisma:seed
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. **Start development server:**
|
|
24
58
|
```bash
|
|
25
59
|
yarn dev
|
|
26
60
|
```
|
|
27
61
|
|
|
28
62
|
The API will be available at `http://localhost:3000`
|
|
29
63
|
|
|
30
|
-
##
|
|
64
|
+
## Creating CRUD APIs
|
|
31
65
|
|
|
32
|
-
|
|
33
|
-
- `yarn build` - Build for production
|
|
34
|
-
- `yarn start` - Start production server
|
|
35
|
-
- `yarn lint` - Run ESLint
|
|
36
|
-
- `yarn type-check` - Run TypeScript type checking
|
|
66
|
+
This template provides a simple way to create type-safe APIs with automatic CRUD operations for your Prisma models.
|
|
37
67
|
|
|
38
|
-
|
|
68
|
+
### 1. Define Your Prisma Model
|
|
39
69
|
|
|
40
|
-
|
|
70
|
+
Add models to `packages/database/schema.prisma`:
|
|
41
71
|
|
|
42
|
-
|
|
72
|
+
```prisma
|
|
73
|
+
model User {
|
|
74
|
+
id String @id @default(cuid())
|
|
75
|
+
email String @unique
|
|
76
|
+
name String
|
|
77
|
+
createdAt DateTime @default(now())
|
|
78
|
+
updatedAt DateTime @updatedAt
|
|
79
|
+
}
|
|
80
|
+
```
|
|
43
81
|
|
|
44
|
-
|
|
45
|
-
- `hello` - Simple greeting endpoint (accepts optional name parameter)
|
|
46
|
-
- `health` - API health check with timestamp
|
|
82
|
+
### 2. Create a Router
|
|
47
83
|
|
|
48
|
-
|
|
49
|
-
- `GET /` - API information
|
|
50
|
-
- `GET /health` - Health check
|
|
84
|
+
Create `src/routers/user.ts`:
|
|
51
85
|
|
|
52
|
-
|
|
86
|
+
```typescript
|
|
87
|
+
import { z } from 'zod';
|
|
88
|
+
import { createCrudRouter } from '../lib/crud.js';
|
|
53
89
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
90
|
+
const createUserSchema = z.object({
|
|
91
|
+
email: z.string().email(),
|
|
92
|
+
name: z.string().min(1),
|
|
93
|
+
});
|
|
58
94
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
95
|
+
const updateUserSchema = z.object({
|
|
96
|
+
email: z.string().email().optional(),
|
|
97
|
+
name: z.string().min(1).optional(),
|
|
98
|
+
});
|
|
63
99
|
|
|
64
|
-
|
|
65
|
-
|
|
100
|
+
export const userRouter = createCrudRouter(
|
|
101
|
+
'user',
|
|
102
|
+
createUserSchema,
|
|
103
|
+
updateUserSchema
|
|
104
|
+
);
|
|
66
105
|
```
|
|
67
106
|
|
|
68
|
-
|
|
107
|
+
### 3. Add to Main Router
|
|
69
108
|
|
|
70
|
-
|
|
109
|
+
Update `src/router/index.ts`:
|
|
71
110
|
|
|
72
|
-
|
|
111
|
+
```typescript
|
|
112
|
+
import { userRouter } from '../routers/user.js';
|
|
73
113
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
114
|
+
export const appRouter = router({
|
|
115
|
+
// ... existing routes
|
|
116
|
+
users: userRouter,
|
|
117
|
+
});
|
|
118
|
+
```
|
|
78
119
|
|
|
79
|
-
|
|
80
|
-
This template includes a powerful controller and middleware system:
|
|
120
|
+
### 4. Use in Frontend
|
|
81
121
|
|
|
82
|
-
1. **Create a Controller:**
|
|
83
122
|
```typescript
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
content: z.string(),
|
|
123
|
+
// Get all users
|
|
124
|
+
const { data: users } = trpc.users.getAll.useQuery();
|
|
125
|
+
|
|
126
|
+
// Create user
|
|
127
|
+
const createUser = trpc.users.create.useMutation();
|
|
128
|
+
await createUser.mutateAsync({
|
|
129
|
+
email: 'user@example.com',
|
|
130
|
+
name: 'John Doe'
|
|
93
131
|
});
|
|
94
132
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Mock data - replace with your database calls
|
|
101
|
-
return [
|
|
102
|
-
{ id: '1', title: 'Post 1', content: 'Content 1', published: true },
|
|
103
|
-
{ id: '2', title: 'Post 2', content: 'Content 2', published: false },
|
|
104
|
-
];
|
|
105
|
-
}
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// Protected endpoint with middleware
|
|
109
|
-
create = this.createMutationWithMiddleware(
|
|
110
|
-
createPostSchema,
|
|
111
|
-
[logger, rateLimit(5, 60000), requireAuth],
|
|
112
|
-
async (input, ctx) => {
|
|
113
|
-
// Mock creation - replace with your database calls
|
|
114
|
-
return { id: '3', ...input, published: false };
|
|
115
|
-
}
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
// Admin-only endpoint
|
|
119
|
-
delete = this.createMutationWithMiddleware(
|
|
120
|
-
z.object({ id: z.string() }),
|
|
121
|
-
[requireAuth, requireAdmin],
|
|
122
|
-
async (input, ctx) => {
|
|
123
|
-
// Mock deletion - replace with your database calls
|
|
124
|
-
return { success: true, deletedId: input.id };
|
|
125
|
-
}
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export const postRouter = controllerToRouter({
|
|
130
|
-
getAll: new PostController({} as any).getAll,
|
|
131
|
-
create: new PostController({} as any).create,
|
|
132
|
-
delete: new PostController({} as any).delete,
|
|
133
|
+
// Update user
|
|
134
|
+
const updateUser = trpc.users.update.useMutation();
|
|
135
|
+
await updateUser.mutateAsync({
|
|
136
|
+
id: 'user-id',
|
|
137
|
+
data: { name: 'Jane Doe' }
|
|
133
138
|
});
|
|
139
|
+
|
|
140
|
+
// Delete user
|
|
141
|
+
const deleteUser = trpc.users.delete.useMutation();
|
|
142
|
+
await deleteUser.mutateAsync({ id: 'user-id' });
|
|
134
143
|
```
|
|
135
144
|
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
// src/router/index.ts
|
|
139
|
-
import { postRouter } from '../controllers/PostController.js';
|
|
145
|
+
## Generated CRUD Operations
|
|
140
146
|
|
|
141
|
-
|
|
142
|
-
posts: postRouter,
|
|
143
|
-
// ... other routes
|
|
144
|
-
});
|
|
145
|
-
```
|
|
147
|
+
Each `createCrudRouter` call generates:
|
|
146
148
|
|
|
147
|
-
|
|
149
|
+
- `getAll({ skip?, take?, orderBy? })` - List with pagination
|
|
150
|
+
- `getById({ id })` - Get single record
|
|
151
|
+
- `create(data)` - Create new record
|
|
152
|
+
- `update({ id, data })` - Update existing record
|
|
153
|
+
- `delete({ id })` - Delete record
|
|
154
|
+
- `count({ where? })` - Count records
|
|
148
155
|
|
|
149
|
-
|
|
150
|
-
- `requireAuth` - Requires Bearer token authentication
|
|
151
|
-
- `requireRole(role)` - Requires specific user role
|
|
152
|
-
- `requireAdmin` - Requires admin role
|
|
156
|
+
## Available Scripts
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
- `
|
|
156
|
-
- `
|
|
157
|
-
- `
|
|
158
|
-
- `
|
|
159
|
-
- `
|
|
158
|
+
- `yarn dev` - Start development server with hot reload
|
|
159
|
+
- `yarn build` - Build production bundle
|
|
160
|
+
- `yarn start` - Start production server
|
|
161
|
+
- `yarn test` - Run tests
|
|
162
|
+
- `yarn lint` - Lint code
|
|
163
|
+
- `yarn type-check` - Check TypeScript types
|
|
160
164
|
|
|
161
|
-
|
|
165
|
+
## Project Structure
|
|
162
166
|
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
```
|
|
168
|
+
src/
|
|
169
|
+
├── router/
|
|
170
|
+
│ └── index.ts # Main router definition
|
|
171
|
+
├── routers/
|
|
172
|
+
│ ├── test.ts # Example CRUD router for Test model
|
|
173
|
+
│ └── user.example.ts # Example CRUD router for User model
|
|
174
|
+
├── lib/
|
|
175
|
+
│ ├── crud.ts # CRUD router generator
|
|
176
|
+
│ └── database.ts # Database connection
|
|
177
|
+
├── context.ts # tRPC context
|
|
178
|
+
├── trpc.ts # tRPC setup
|
|
179
|
+
└── server.ts # Express server setup
|
|
180
|
+
```
|
|
166
181
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
182
|
+
## Advanced Usage
|
|
183
|
+
|
|
184
|
+
### Custom Procedures
|
|
185
|
+
|
|
186
|
+
Extend generated routers with custom procedures:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { router, publicProcedure } from '../trpc.js';
|
|
190
|
+
import { createCrudRouter } from '../lib/crud.js';
|
|
191
|
+
|
|
192
|
+
const baseCrudRouter = createCrudRouter('user', createUserSchema);
|
|
193
|
+
|
|
194
|
+
export const userRouter = router({
|
|
195
|
+
...baseCrudRouter,
|
|
196
|
+
|
|
197
|
+
// Add custom procedures
|
|
198
|
+
getByEmail: publicProcedure
|
|
199
|
+
.input(z.object({ email: z.string().email() }))
|
|
200
|
+
.query(async ({ input }) => {
|
|
201
|
+
return await db.user.findUnique({
|
|
202
|
+
where: { email: input.email }
|
|
203
|
+
});
|
|
171
204
|
}),
|
|
172
|
-
],
|
|
173
205
|
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Authentication
|
|
209
|
+
|
|
210
|
+
For protected routes, you can add authentication middleware to the tRPC setup or create protected procedures:
|
|
174
211
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
212
|
+
```typescript
|
|
213
|
+
import { protectedProcedure } from '../trpc.js';
|
|
214
|
+
|
|
215
|
+
// Use protectedProcedure instead of publicProcedure in your CRUD router
|
|
178
216
|
```
|
|
179
217
|
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
1. **Add New Model**:
|
|
221
|
+
- Add to Prisma schema
|
|
222
|
+
- Run `yarn db:migrate`
|
|
223
|
+
- Create router with `createCrudRouter`
|
|
224
|
+
- Add to main router
|
|
225
|
+
|
|
226
|
+
2. **Test API**:
|
|
227
|
+
- Start development: `yarn dev`
|
|
228
|
+
- API available at `http://localhost:3000/trpc`
|
|
229
|
+
|
|
230
|
+
3. **Type Safety**:
|
|
231
|
+
- All operations are fully type-safe
|
|
232
|
+
- Frontend gets autocomplete and validation
|
|
233
|
+
- Schemas ensure data integrity
|
|
234
|
+
|
|
235
|
+
This simplified approach removes controller complexity while maintaining full type safety and providing powerful CRUD operations for all your Prisma models.
|
|
236
|
+
|
|
180
237
|
## Environment Variables
|
|
181
238
|
|
|
182
|
-
|
|
239
|
+
Create a `.env` file with:
|
|
183
240
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
241
|
+
```env
|
|
242
|
+
# Database
|
|
243
|
+
DATABASE_URL="file:./dev.db"
|
|
187
244
|
|
|
188
|
-
|
|
245
|
+
# API
|
|
246
|
+
PORT=3000
|
|
247
|
+
NODE_ENV=development
|
|
189
248
|
|
|
190
|
-
|
|
191
|
-
|
|
249
|
+
# Add your environment variables here
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Testing
|
|
192
253
|
|
|
193
|
-
|
|
254
|
+
The project includes Jest for testing:
|
|
194
255
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
256
|
+
```typescript
|
|
257
|
+
import { createContext } from '../src/context.js';
|
|
258
|
+
import { appRouter } from '../src/router/index.js';
|
|
259
|
+
|
|
260
|
+
describe('API Tests', () => {
|
|
261
|
+
test('should get test records', async () => {
|
|
262
|
+
const ctx = createContext({} as any);
|
|
263
|
+
const caller = appRouter.createCaller(ctx);
|
|
264
|
+
|
|
265
|
+
const tests = await caller.test.getAll({});
|
|
266
|
+
expect(tests).toBeDefined();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Run tests with:
|
|
272
|
+
```bash
|
|
273
|
+
yarn test
|
|
274
|
+
```
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"version": "{{version}}",
|
|
4
4
|
"description": "{{description}}",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
7
|
-
"types": "
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"types": "./
|
|
11
|
-
"import": "./
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"import": "./src/index.ts"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"type-check": "tsc --noEmit"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@trpc/server": "^
|
|
26
|
+
"@trpc/server": "^11.5.1",
|
|
27
27
|
"cors": "^2.8.5",
|
|
28
28
|
"dotenv": "^16.3.1",
|
|
29
29
|
"express": "^4.18.2",
|
|
File without changes
|
|
@@ -5,10 +5,5 @@ export type { AppRouter } from './router/index.js';
|
|
|
5
5
|
// Export context type for client usage
|
|
6
6
|
export type { Context } from './context.js';
|
|
7
7
|
|
|
8
|
-
// Export
|
|
9
|
-
export
|
|
10
|
-
export * from './middleware/common.js';
|
|
11
|
-
|
|
12
|
-
// Export controller base classes for extensions
|
|
13
|
-
export { BaseController, controllerToRouter } from './lib/controller.js';
|
|
14
|
-
export type { MiddlewareFn } from './lib/controller.js';
|
|
8
|
+
// Export CRUD utilities for creating routers
|
|
9
|
+
export { createCrudRouter } from './lib/crud.js';
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Prisma } from '@{{workspaceScope}}/database/client';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { prisma } from '../lib/database.js';
|
|
4
|
+
import { publicProcedure, router } from '../trpc.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a standard CRUD router for any Prisma model
|
|
8
|
+
*
|
|
9
|
+
* @param modelName - The name of the Prisma model (e.g., 'user', 'post', 'test')
|
|
10
|
+
* @param createSchema - Zod schema for creating new records
|
|
11
|
+
* @param updateSchema - Zod schema for updating records (optional, defaults to createSchema.partial())
|
|
12
|
+
* @returns tRPC router with standard CRUD operations
|
|
13
|
+
*/
|
|
14
|
+
export function createCrudRouter<
|
|
15
|
+
TModelName extends Prisma.ModelName,
|
|
16
|
+
TCreateInput extends Record<string, any>,
|
|
17
|
+
TUpdateInput extends Record<string, any> = Partial<TCreateInput>
|
|
18
|
+
>(
|
|
19
|
+
modelName: TModelName,
|
|
20
|
+
createSchema: z.ZodSchema<TCreateInput>,
|
|
21
|
+
updateSchema?: z.ZodSchema<TUpdateInput>
|
|
22
|
+
) {
|
|
23
|
+
const model = (prisma as any)[modelName];
|
|
24
|
+
const updateSchemaToUse = updateSchema || createSchema.partial();
|
|
25
|
+
|
|
26
|
+
return router({
|
|
27
|
+
// Get all records
|
|
28
|
+
getAll: publicProcedure
|
|
29
|
+
.input(z.object({
|
|
30
|
+
skip: z.number().min(0).optional(),
|
|
31
|
+
take: z.number().min(1).max(100).optional(),
|
|
32
|
+
orderBy: z.record(z.enum(['asc', 'desc'])).optional(),
|
|
33
|
+
}))
|
|
34
|
+
.query(async ({ input }) => {
|
|
35
|
+
return await model.findMany({
|
|
36
|
+
skip: input.skip,
|
|
37
|
+
take: input.take || 10,
|
|
38
|
+
orderBy: input.orderBy,
|
|
39
|
+
});
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
// Get record by ID
|
|
43
|
+
getById: publicProcedure
|
|
44
|
+
.input(z.object({ id: z.string() }))
|
|
45
|
+
.query(async ({ input }) => {
|
|
46
|
+
const record = await model.findUnique({
|
|
47
|
+
where: { id: input.id },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!record) {
|
|
51
|
+
throw new Error(`${modelName} not found`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return record;
|
|
55
|
+
}),
|
|
56
|
+
|
|
57
|
+
// Create new record
|
|
58
|
+
create: publicProcedure
|
|
59
|
+
.input(createSchema)
|
|
60
|
+
.mutation(async ({ input }) => {
|
|
61
|
+
return await model.create({
|
|
62
|
+
data: input,
|
|
63
|
+
});
|
|
64
|
+
}),
|
|
65
|
+
|
|
66
|
+
// Update record
|
|
67
|
+
update: publicProcedure
|
|
68
|
+
.input(z.object({
|
|
69
|
+
id: z.string(),
|
|
70
|
+
data: updateSchemaToUse,
|
|
71
|
+
}))
|
|
72
|
+
.mutation(async ({ input }) => {
|
|
73
|
+
const existingRecord = await model.findUnique({
|
|
74
|
+
where: { id: input.id },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!existingRecord) {
|
|
78
|
+
throw new Error(`${modelName} not found`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return await model.update({
|
|
82
|
+
where: { id: input.id },
|
|
83
|
+
data: input.data,
|
|
84
|
+
});
|
|
85
|
+
}),
|
|
86
|
+
|
|
87
|
+
// Delete record
|
|
88
|
+
delete: publicProcedure
|
|
89
|
+
.input(z.object({ id: z.string() }))
|
|
90
|
+
.mutation(async ({ input }) => {
|
|
91
|
+
const existingRecord = await model.findUnique({
|
|
92
|
+
where: { id: input.id },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!existingRecord) {
|
|
96
|
+
throw new Error(`${modelName} not found`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return await model.delete({
|
|
100
|
+
where: { id: input.id },
|
|
101
|
+
});
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
// Get count
|
|
105
|
+
count: publicProcedure
|
|
106
|
+
.input(z.object({
|
|
107
|
+
where: z.record(z.any()).optional(),
|
|
108
|
+
}))
|
|
109
|
+
.query(async ({ input }) => {
|
|
110
|
+
return await model.count({
|
|
111
|
+
where: input.where,
|
|
112
|
+
});
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Example usage:
|
|
119
|
+
*
|
|
120
|
+
* ```typescript
|
|
121
|
+
* import { z } from 'zod';
|
|
122
|
+
* import { createCrudRouter } from '../lib/crud.js';
|
|
123
|
+
*
|
|
124
|
+
* // Define schemas for your model
|
|
125
|
+
* const createUserSchema = z.object({
|
|
126
|
+
* email: z.string().email(),
|
|
127
|
+
* name: z.string(),
|
|
128
|
+
* });
|
|
129
|
+
*
|
|
130
|
+
* const updateUserSchema = z.object({
|
|
131
|
+
* email: z.string().email().optional(),
|
|
132
|
+
* name: z.string().optional(),
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* // Create the CRUD router
|
|
136
|
+
* export const userRouter = createCrudRouter(
|
|
137
|
+
* 'user',
|
|
138
|
+
* createUserSchema,
|
|
139
|
+
* updateUserSchema
|
|
140
|
+
* );
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* This will generate:
|
|
144
|
+
* - users.getAll() - Get all users with pagination
|
|
145
|
+
* - users.getById({ id }) - Get user by ID
|
|
146
|
+
* - users.create({ email, name }) - Create new user
|
|
147
|
+
* - users.update({ id, data: { email?, name? } }) - Update user
|
|
148
|
+
* - users.delete({ id }) - Delete user
|
|
149
|
+
* - users.count() - Get user count
|
|
150
|
+
*/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PrismaClient } from '@{{workspaceScope}}/database/client';
|
|
2
|
+
|
|
3
|
+
// Initialize Prisma Client
|
|
4
|
+
declare global {
|
|
5
|
+
// eslint-disable-next-line no-var
|
|
6
|
+
var __prisma: PrismaClient | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Prevent multiple instances of Prisma Client in development
|
|
10
|
+
export const prisma = globalThis.__prisma || new PrismaClient({
|
|
11
|
+
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
15
|
+
globalThis.__prisma = prisma;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Graceful shutdown
|
|
19
|
+
process.on('beforeExit', async () => {
|
|
20
|
+
await prisma.$disconnect();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export default prisma;
|