@idealyst/cli 1.0.92 → 1.0.94
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/utils.js +63 -20
- package/dist/generators/utils.js.map +1 -1
- package/dist/template/packages/api/README.md +400 -164
- package/dist/template/packages/api/package.json +11 -1
- package/dist/template/packages/api/src/context.ts +35 -2
- package/dist/template/packages/api/src/graphql/builder.ts +75 -0
- package/dist/template/packages/api/src/graphql/generated.ts +64 -0
- package/dist/template/packages/api/src/graphql/index.ts +75 -0
- package/dist/template/packages/api/src/graphql/types/index.ts +44 -0
- package/dist/template/packages/api/src/graphql/types/test.ts +245 -0
- package/dist/template/packages/api/src/index.ts +20 -3
- package/dist/template/packages/api/src/lib/database.ts +1 -1
- package/dist/template/packages/api/src/routers/test.ts +140 -38
- package/dist/template/packages/api/src/server.ts +23 -5
- package/dist/template/packages/api/tsconfig.json +1 -0
- package/dist/template/packages/shared/package.json +6 -0
- package/dist/template/packages/shared/src/components/App.tsx +13 -2
- package/dist/template/packages/shared/src/components/HelloWorld.tsx +333 -106
- package/dist/template/packages/shared/src/graphql/client.ts +34 -0
- package/dist/template/packages/shared/src/index.ts +8 -0
- package/dist/template/packages/web/vite.config.ts +1 -1
- package/package.json +1 -1
- package/template/packages/api/README.md +400 -164
- package/template/packages/api/package.json +11 -1
- package/template/packages/api/src/context.ts +35 -2
- package/template/packages/api/src/graphql/builder.ts +75 -0
- package/template/packages/api/src/graphql/generated.ts +64 -0
- package/template/packages/api/src/graphql/index.ts +75 -0
- package/template/packages/api/src/graphql/types/index.ts +44 -0
- package/template/packages/api/src/graphql/types/test.ts +245 -0
- package/template/packages/api/src/index.ts +20 -3
- package/template/packages/api/src/lib/database.ts +1 -1
- package/template/packages/api/src/routers/test.ts +140 -38
- package/template/packages/api/src/server.ts +23 -5
- package/template/packages/api/tsconfig.json +1 -0
- package/template/packages/shared/package.json +6 -0
- package/template/packages/shared/src/components/App.tsx +13 -2
- package/template/packages/shared/src/components/HelloWorld.tsx +333 -106
- package/template/packages/shared/src/graphql/client.ts +34 -0
- package/template/packages/shared/src/index.ts +8 -0
- package/template/packages/web/vite.config.ts +1 -1
- package/dist/template/packages/api/src/lib/crud.ts +0 -150
- package/dist/template/packages/api/src/routers/user.example.ts +0 -83
- package/template/packages/api/src/lib/crud.ts +0 -150
- package/template/packages/api/src/routers/user.example.ts +0 -83
|
@@ -1,57 +1,159 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
// Define Zod schemas for the Test model based on the Prisma schema
|
|
5
|
-
const createTestSchema = z.object({
|
|
6
|
-
name: z.string().min(1, "Name is required"),
|
|
7
|
-
message: z.string().min(1, "Message is required"),
|
|
8
|
-
status: z.string().optional().default("active"),
|
|
9
|
-
});
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { prisma } from '../lib/database.js';
|
|
3
|
+
import { publicProcedure, router } from '../trpc.js';
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Test Router - tRPC CRUD operations for the Test model
|
|
7
|
+
*
|
|
8
|
+
* This router provides standard CRUD operations via tRPC.
|
|
9
|
+
* For the GraphQL equivalent, see src/graphql/types/test.ts
|
|
10
|
+
*
|
|
11
|
+
* Endpoints:
|
|
12
|
+
* - test.getById({ id }) - Get test by ID
|
|
13
|
+
* - test.getAll({ skip, take }) - Get all tests with pagination
|
|
14
|
+
* - test.count({ status }) - Get count of tests
|
|
15
|
+
* - test.create({ name, message }) - Create new test
|
|
16
|
+
* - test.update({ id, data }) - Update existing test
|
|
17
|
+
* - test.delete({ id }) - Delete test
|
|
18
|
+
*/
|
|
19
|
+
export const testRouter = router({
|
|
20
|
+
// Get a single test by ID
|
|
21
|
+
getById: publicProcedure
|
|
22
|
+
.input(z.object({ id: z.string() }))
|
|
23
|
+
.query(async ({ input }) => {
|
|
24
|
+
const test = await prisma.test.findUnique({
|
|
25
|
+
where: { id: input.id },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!test) {
|
|
29
|
+
throw new Error('Test not found');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return test;
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
// Get all tests with pagination
|
|
36
|
+
getAll: publicProcedure
|
|
37
|
+
.input(z.object({
|
|
38
|
+
skip: z.number().min(0).optional(),
|
|
39
|
+
take: z.number().min(1).max(100).optional(),
|
|
40
|
+
orderBy: z.enum(['createdAt', 'updatedAt', 'name']).optional(),
|
|
41
|
+
orderDir: z.enum(['asc', 'desc']).optional(),
|
|
42
|
+
}))
|
|
43
|
+
.query(async ({ input }) => {
|
|
44
|
+
const take = input.take ?? 10;
|
|
45
|
+
const orderField = input.orderBy ?? 'createdAt';
|
|
46
|
+
const orderDir = input.orderDir ?? 'desc';
|
|
47
|
+
|
|
48
|
+
return prisma.test.findMany({
|
|
49
|
+
...(input.skip != null ? { skip: input.skip } : {}),
|
|
50
|
+
take,
|
|
51
|
+
orderBy: { [orderField]: orderDir },
|
|
52
|
+
});
|
|
53
|
+
}),
|
|
54
|
+
|
|
55
|
+
// Get count of tests
|
|
56
|
+
count: publicProcedure
|
|
57
|
+
.input(z.object({
|
|
58
|
+
status: z.string().optional(),
|
|
59
|
+
}))
|
|
60
|
+
.query(async ({ input }) => {
|
|
61
|
+
return prisma.test.count({
|
|
62
|
+
...(input.status != null ? { where: { status: input.status } } : {}),
|
|
63
|
+
});
|
|
64
|
+
}),
|
|
16
65
|
|
|
17
|
-
// Create
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
66
|
+
// Create a new test
|
|
67
|
+
create: publicProcedure
|
|
68
|
+
.input(z.object({
|
|
69
|
+
name: z.string().min(1, 'Name is required'),
|
|
70
|
+
message: z.string().min(1, 'Message is required'),
|
|
71
|
+
status: z.string().optional().default('active'),
|
|
72
|
+
}))
|
|
73
|
+
.mutation(async ({ input }) => {
|
|
74
|
+
return prisma.test.create({
|
|
75
|
+
data: {
|
|
76
|
+
name: input.name,
|
|
77
|
+
message: input.message,
|
|
78
|
+
status: input.status,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}),
|
|
82
|
+
|
|
83
|
+
// Update an existing test
|
|
84
|
+
update: publicProcedure
|
|
85
|
+
.input(z.object({
|
|
86
|
+
id: z.string(),
|
|
87
|
+
data: z.object({
|
|
88
|
+
name: z.string().min(1).optional(),
|
|
89
|
+
message: z.string().min(1).optional(),
|
|
90
|
+
status: z.string().optional(),
|
|
91
|
+
}),
|
|
92
|
+
}))
|
|
93
|
+
.mutation(async ({ input }) => {
|
|
94
|
+
const existing = await prisma.test.findUnique({
|
|
95
|
+
where: { id: input.id },
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!existing) {
|
|
99
|
+
throw new Error('Test not found');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Build update data only with defined values
|
|
103
|
+
const updateData: { name?: string; message?: string; status?: string } = {};
|
|
104
|
+
if (input.data.name != null) updateData.name = input.data.name;
|
|
105
|
+
if (input.data.message != null) updateData.message = input.data.message;
|
|
106
|
+
if (input.data.status != null) updateData.status = input.data.status;
|
|
107
|
+
|
|
108
|
+
return prisma.test.update({
|
|
109
|
+
where: { id: input.id },
|
|
110
|
+
data: updateData,
|
|
111
|
+
});
|
|
112
|
+
}),
|
|
113
|
+
|
|
114
|
+
// Delete a test
|
|
115
|
+
delete: publicProcedure
|
|
116
|
+
.input(z.object({ id: z.string() }))
|
|
117
|
+
.mutation(async ({ input }) => {
|
|
118
|
+
const existing = await prisma.test.findUnique({
|
|
119
|
+
where: { id: input.id },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!existing) {
|
|
123
|
+
throw new Error('Test not found');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return prisma.test.delete({
|
|
127
|
+
where: { id: input.id },
|
|
128
|
+
});
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
23
131
|
|
|
24
132
|
/**
|
|
25
|
-
* This generates the following endpoints:
|
|
26
|
-
*
|
|
27
|
-
* - test.getAll({ skip?, take?, orderBy? }) - Get all test records with pagination
|
|
28
|
-
* - test.getById({ id }) - Get test record by ID
|
|
29
|
-
* - test.create({ name, message, status? }) - Create new test record
|
|
30
|
-
* - test.update({ id, data: { name?, message?, status? } }) - Update test record
|
|
31
|
-
* - test.delete({ id }) - Delete test record
|
|
32
|
-
* - test.count({ where? }) - Get test record count
|
|
33
|
-
*
|
|
34
133
|
* Example usage in frontend:
|
|
35
|
-
*
|
|
134
|
+
*
|
|
36
135
|
* ```typescript
|
|
37
136
|
* // Get all tests
|
|
38
137
|
* const { data: tests } = trpc.test.getAll.useQuery({ take: 10 });
|
|
39
|
-
*
|
|
138
|
+
*
|
|
139
|
+
* // Get test by ID
|
|
140
|
+
* const { data: test } = trpc.test.getById.useQuery({ id: 'test-id' });
|
|
141
|
+
*
|
|
40
142
|
* // Create a new test
|
|
41
143
|
* const createTest = trpc.test.create.useMutation();
|
|
42
|
-
* await createTest.mutateAsync({
|
|
43
|
-
* name: 'My Test',
|
|
144
|
+
* await createTest.mutateAsync({
|
|
145
|
+
* name: 'My Test',
|
|
44
146
|
* message: 'This is a test message',
|
|
45
147
|
* status: 'active'
|
|
46
148
|
* });
|
|
47
|
-
*
|
|
149
|
+
*
|
|
48
150
|
* // Update a test
|
|
49
151
|
* const updateTest = trpc.test.update.useMutation();
|
|
50
|
-
* await updateTest.mutateAsync({
|
|
51
|
-
* id: 'test-id',
|
|
52
|
-
* data: { name: 'Updated Test' }
|
|
152
|
+
* await updateTest.mutateAsync({
|
|
153
|
+
* id: 'test-id',
|
|
154
|
+
* data: { name: 'Updated Test' }
|
|
53
155
|
* });
|
|
54
|
-
*
|
|
156
|
+
*
|
|
55
157
|
* // Delete a test
|
|
56
158
|
* const deleteTest = trpc.test.delete.useMutation();
|
|
57
159
|
* await deleteTest.mutateAsync({ id: 'test-id' });
|
|
@@ -3,6 +3,7 @@ import cors from 'cors';
|
|
|
3
3
|
import { createExpressMiddleware } from '@trpc/server/adapters/express';
|
|
4
4
|
import { appRouter } from './router/index.js';
|
|
5
5
|
import { createContext } from './context.js';
|
|
6
|
+
import { yoga } from './graphql/index.js';
|
|
6
7
|
import dotenv from 'dotenv';
|
|
7
8
|
|
|
8
9
|
// Load environment variables
|
|
@@ -17,12 +18,15 @@ app.use(cors({
|
|
|
17
18
|
credentials: true,
|
|
18
19
|
}));
|
|
19
20
|
|
|
21
|
+
// Parse JSON bodies (needed for some GraphQL clients)
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
|
|
20
24
|
// Health check endpoint
|
|
21
|
-
app.get('/health', (
|
|
25
|
+
app.get('/health', (_req, res) => {
|
|
22
26
|
res.json({ status: 'OK', timestamp: new Date().toISOString() });
|
|
23
27
|
});
|
|
24
28
|
|
|
25
|
-
// tRPC middleware
|
|
29
|
+
// tRPC middleware - for internal TypeScript clients
|
|
26
30
|
app.use(
|
|
27
31
|
'/trpc',
|
|
28
32
|
createExpressMiddleware({
|
|
@@ -31,20 +35,34 @@ app.use(
|
|
|
31
35
|
})
|
|
32
36
|
);
|
|
33
37
|
|
|
38
|
+
// GraphQL middleware - for public API, mobile clients, etc.
|
|
39
|
+
// GraphQL Yoga handles the /graphql endpoint
|
|
40
|
+
// Using all() to handle both GET (GraphiQL) and POST (queries)
|
|
41
|
+
app.all('/graphql', yoga as unknown as express.RequestHandler);
|
|
42
|
+
|
|
34
43
|
// Default route
|
|
35
|
-
app.get('/', (
|
|
44
|
+
app.get('/', (_req, res) => {
|
|
36
45
|
res.json({
|
|
37
46
|
message: 'Welcome to {{projectName}} API',
|
|
38
47
|
endpoints: {
|
|
39
48
|
health: '/health',
|
|
40
49
|
trpc: '/trpc',
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
graphql: '/graphql',
|
|
51
|
+
graphiql: process.env.NODE_ENV !== 'production' ? '/graphql' : undefined,
|
|
52
|
+
},
|
|
53
|
+
documentation: {
|
|
54
|
+
trpc: 'Use tRPC for internal TypeScript clients with full type safety',
|
|
55
|
+
graphql: 'Use GraphQL for public APIs, mobile apps, and third-party integrations',
|
|
56
|
+
},
|
|
43
57
|
});
|
|
44
58
|
});
|
|
45
59
|
|
|
46
60
|
app.listen(PORT, () => {
|
|
47
61
|
console.log(`🚀 Server running on http://localhost:${PORT}`);
|
|
48
62
|
console.log(`📡 tRPC API available at http://localhost:${PORT}/trpc`);
|
|
63
|
+
console.log(`📊 GraphQL API available at http://localhost:${PORT}/graphql`);
|
|
64
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
65
|
+
console.log(`🎮 GraphiQL Playground at http://localhost:${PORT}/graphql`);
|
|
66
|
+
}
|
|
49
67
|
console.log(`🏥 Health check at http://localhost:${PORT}/health`);
|
|
50
68
|
});
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
"optional": true
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@mdi/js": "^7.4.47",
|
|
40
|
+
"@mdi/react": "^1.6.1",
|
|
41
|
+
"graphql": "^16.9.0",
|
|
42
|
+
"graphql-request": "^7.1.2"
|
|
43
|
+
},
|
|
38
44
|
"devDependencies": {
|
|
39
45
|
"@idealyst/components": "^{{idealystVersion}}",
|
|
40
46
|
"@idealyst/navigation": "^{{idealystVersion}}",
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { NavigatorProvider } from "@idealyst/navigation";
|
|
2
2
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3
|
-
import React from "react";
|
|
3
|
+
import React, { useEffect } from "react";
|
|
4
|
+
import { createGraphQLClient } from "../graphql/client";
|
|
4
5
|
import AppRouter from "../navigation/AppRouter";
|
|
5
6
|
import { createTRPCClient, trpc } from "../trpc/client";
|
|
6
7
|
|
|
7
8
|
interface AppProps {
|
|
8
9
|
apiUrl?: string;
|
|
10
|
+
graphqlUrl?: string;
|
|
9
11
|
queryClient?: QueryClient;
|
|
10
12
|
headers?: () => Record<string, string>;
|
|
11
13
|
}
|
|
@@ -20,11 +22,12 @@ const defaultQueryClient = new QueryClient({
|
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
|
-
* Unified App component that sets up tRPC, React Query providers, and Navigation
|
|
25
|
+
* Unified App component that sets up tRPC, GraphQL, React Query providers, and Navigation
|
|
24
26
|
* This component can be used by both web and mobile platforms
|
|
25
27
|
*/
|
|
26
28
|
export const App: React.FC<AppProps> = ({
|
|
27
29
|
apiUrl = "http://localhost:3000/trpc",
|
|
30
|
+
graphqlUrl = "http://localhost:3000/graphql",
|
|
28
31
|
queryClient = defaultQueryClient,
|
|
29
32
|
headers,
|
|
30
33
|
}) => {
|
|
@@ -34,6 +37,14 @@ export const App: React.FC<AppProps> = ({
|
|
|
34
37
|
headers,
|
|
35
38
|
});
|
|
36
39
|
|
|
40
|
+
// Initialize GraphQL client
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
createGraphQLClient({
|
|
43
|
+
apiUrl: graphqlUrl,
|
|
44
|
+
headers,
|
|
45
|
+
});
|
|
46
|
+
}, [graphqlUrl, headers]);
|
|
47
|
+
|
|
37
48
|
return (
|
|
38
49
|
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
|
39
50
|
<QueryClientProvider client={queryClient}>
|