nuxt-procedures 0.1.5 → 0.1.6
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 +30 -37
- package/dist/module.json +1 -1
- package/dist/module.mjs +29 -9
- package/dist/runtime/create-caller.js +2 -2
- package/dist/runtime/handler.d.ts +2 -0
- package/dist/runtime/handler.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,9 +27,11 @@ Install and configure the module with a single command:
|
|
|
27
27
|
```bash
|
|
28
28
|
npx nuxi module add nuxt-procedures
|
|
29
29
|
```
|
|
30
|
+
|
|
30
31
|
This will add `nuxt-procedures` to your `package.json` and `nuxt.config.ts`.
|
|
31
32
|
|
|
32
33
|
You also need to install its peer dependency, `zod`:
|
|
34
|
+
|
|
33
35
|
```bash
|
|
34
36
|
npm install zod
|
|
35
37
|
```
|
|
@@ -46,46 +48,48 @@ npm install nuxt-procedures zod
|
|
|
46
48
|
|
|
47
49
|
```typescript
|
|
48
50
|
export default defineNuxtConfig({
|
|
49
|
-
modules: [
|
|
50
|
-
})
|
|
51
|
+
modules: ["nuxt-procedures"],
|
|
52
|
+
});
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
## Usage
|
|
54
56
|
|
|
55
57
|
### 1. Define Procedures
|
|
56
58
|
|
|
57
|
-
Create `.ts` files in your `server/
|
|
59
|
+
Create `.ts` files in your `server/procedures` directory. The module will automatically create a corresponding client for each file.
|
|
58
60
|
|
|
59
61
|
#### Simple Example
|
|
60
62
|
|
|
61
63
|
For a simple input and output, you can use Zod schemas directly.
|
|
62
64
|
|
|
63
|
-
`server/
|
|
65
|
+
`server/procedures/hello.ts`:
|
|
66
|
+
|
|
64
67
|
```typescript
|
|
65
|
-
import { z } from
|
|
68
|
+
import { z } from "zod";
|
|
66
69
|
|
|
67
70
|
export default defineProcedure({
|
|
68
71
|
input: z.string(),
|
|
69
72
|
output: z.string(),
|
|
70
73
|
handler: async ({ input }) => {
|
|
71
|
-
return `Hello, ${input}
|
|
74
|
+
return `Hello, ${input}!`;
|
|
72
75
|
},
|
|
73
|
-
})
|
|
76
|
+
});
|
|
74
77
|
```
|
|
75
78
|
|
|
76
79
|
#### Complex Example
|
|
77
80
|
|
|
78
81
|
For more complex scenarios, `z.object` is the way to go. This is useful for things like form submissions or creating database entries.
|
|
79
82
|
|
|
80
|
-
`server/
|
|
83
|
+
`server/procedures/users/create.ts`:
|
|
84
|
+
|
|
81
85
|
```typescript
|
|
82
|
-
import { z } from
|
|
86
|
+
import { z } from "zod";
|
|
83
87
|
|
|
84
88
|
export default defineProcedure({
|
|
85
89
|
input: z.object({
|
|
86
90
|
name: z.string(),
|
|
87
91
|
email: z.string().email(),
|
|
88
|
-
role: z.enum([
|
|
92
|
+
role: z.enum(["admin", "user"]).default("user"),
|
|
89
93
|
}),
|
|
90
94
|
output: z.object({
|
|
91
95
|
id: z.string(),
|
|
@@ -95,33 +99,34 @@ export default defineProcedure({
|
|
|
95
99
|
handler: async ({ input, event }) => {
|
|
96
100
|
// The event param gives you access to the request context and Nuxt utilities
|
|
97
101
|
// For example, you can access request headers
|
|
98
|
-
const headers = getRequestHeaders(event)
|
|
99
|
-
console.log(headers)
|
|
102
|
+
const headers = getRequestHeaders(event);
|
|
103
|
+
console.log(headers);
|
|
100
104
|
|
|
101
105
|
// In a real app, you would create a user in your database
|
|
102
106
|
// The following is just an example of how you could get a db client
|
|
103
|
-
const db = await useDB(event)
|
|
107
|
+
const db = await useDB(event);
|
|
104
108
|
const newUser = await db.user.create({
|
|
105
109
|
data: input,
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
return newUser
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return newUser;
|
|
109
113
|
},
|
|
110
|
-
})
|
|
114
|
+
});
|
|
111
115
|
```
|
|
112
116
|
|
|
113
117
|
### 2. Use the `apiClient`
|
|
114
118
|
|
|
115
|
-
The module automatically generates an `apiClient` that you can use in your components or pages. The structure of the `apiClient` mirrors your `server/
|
|
119
|
+
The module automatically generates an `apiClient` that you can use in your components or pages. The structure of the `apiClient` mirrors your `server/procedures` directory.
|
|
116
120
|
|
|
117
121
|
#### `useCall`
|
|
118
122
|
|
|
119
123
|
The `useCall` method is a wrapper around Nuxt's `useFetch` and is the recommended way to call procedures from your Vue components.
|
|
120
124
|
|
|
121
125
|
Calling the simple `hello` procedure:
|
|
126
|
+
|
|
122
127
|
```vue
|
|
123
128
|
<script setup lang="ts">
|
|
124
|
-
const { data: greeting, pending } = await apiClient.hello.useCall(
|
|
129
|
+
const { data: greeting, pending } = await apiClient.hello.useCall("World");
|
|
125
130
|
</script>
|
|
126
131
|
|
|
127
132
|
<template>
|
|
@@ -130,33 +135,21 @@ const { data: greeting, pending } = await apiClient.hello.useCall('World')
|
|
|
130
135
|
</template>
|
|
131
136
|
```
|
|
132
137
|
|
|
133
|
-
Calling the complex `users/create` procedure:
|
|
134
|
-
```vue
|
|
135
|
-
<script setup lang="ts">
|
|
136
|
-
const { data: newUser, execute } = apiClient.users.create.useCall({
|
|
137
|
-
name: 'Andres',
|
|
138
|
-
email: 'andres@example.com',
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
// `execute` can be called later, e.g. in a form submission handler
|
|
142
|
-
// const submitForm = () => execute()
|
|
143
|
-
</script>
|
|
144
|
-
```
|
|
145
|
-
|
|
146
138
|
#### `call`
|
|
147
139
|
|
|
148
|
-
The `call` method makes a direct API call
|
|
140
|
+
The `call` method makes a direct API call, without storing the result in the Nuxt payload as `useFetch` would.
|
|
141
|
+
You would normally call procedures this way when you don't need to load data for the initial render of the component, but instead when triggering them in response to a form submission or some other event, same as when you would use `$fetch`.
|
|
149
142
|
|
|
150
143
|
```typescript
|
|
151
144
|
// Calling the simple procedure
|
|
152
|
-
const greeting = await apiClient.hello.call(
|
|
145
|
+
const greeting = await apiClient.hello.call("World");
|
|
153
146
|
// greeting is "Hello, World!"
|
|
154
147
|
|
|
155
148
|
// Calling the complex procedure
|
|
156
149
|
const newUser = await apiClient.users.create.call({
|
|
157
|
-
name:
|
|
158
|
-
email:
|
|
159
|
-
})
|
|
150
|
+
name: "Andres",
|
|
151
|
+
email: "andres@example.com",
|
|
152
|
+
});
|
|
160
153
|
// newUser is { id: '...', name: 'Andres', email: 'andres@example.com' }
|
|
161
154
|
```
|
|
162
155
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join, parse, sep } from 'node:path';
|
|
2
|
-
import { defineNuxtModule, createResolver, addServerImports, addTemplate, updateTemplates, addImports, resolveFiles } from '@nuxt/kit';
|
|
2
|
+
import { defineNuxtModule, createResolver, addServerImports, addTemplate, addServerHandler, updateTemplates, addImports, resolveFiles } from '@nuxt/kit';
|
|
3
3
|
import { camelCase } from 'scule';
|
|
4
4
|
|
|
5
5
|
const toCode = Symbol("toCode");
|
|
@@ -48,15 +48,25 @@ const module = defineNuxtModule({
|
|
|
48
48
|
}
|
|
49
49
|
]);
|
|
50
50
|
const createCallerPath = resolve("./runtime/create-caller.ts");
|
|
51
|
+
const handlerPath = resolve("./runtime/handler.ts");
|
|
51
52
|
const generatedClient = addTemplate({
|
|
52
53
|
filename: "nuxt-procedures/api-client.ts",
|
|
53
54
|
getContents: () => generateClientCode(nuxt, createCallerPath),
|
|
54
55
|
write: true
|
|
55
56
|
});
|
|
57
|
+
const generatedHandler = addTemplate({
|
|
58
|
+
filename: "nuxt-procedures/server-handler.ts",
|
|
59
|
+
getContents: () => generateHandlerCode(nuxt, handlerPath),
|
|
60
|
+
write: true
|
|
61
|
+
});
|
|
62
|
+
addServerHandler({
|
|
63
|
+
route: "/procedures/**",
|
|
64
|
+
handler: generatedHandler.dst
|
|
65
|
+
});
|
|
56
66
|
nuxt.hook("builder:watch", async (event, path) => {
|
|
57
|
-
if (path.includes("
|
|
67
|
+
if (path.includes("procedures")) {
|
|
58
68
|
updateTemplates({
|
|
59
|
-
filter: (t) => t.filename === generatedClient.filename
|
|
69
|
+
filter: (t) => t.filename === generatedClient.filename || t.filename === generatedHandler.filename
|
|
60
70
|
});
|
|
61
71
|
}
|
|
62
72
|
});
|
|
@@ -66,10 +76,7 @@ const module = defineNuxtModule({
|
|
|
66
76
|
});
|
|
67
77
|
async function buildRouterStructure(nuxt) {
|
|
68
78
|
const procedureDirs = nuxt.options._layers.map(
|
|
69
|
-
(l) => (
|
|
70
|
-
// join(l.config.serverDir ?? "server", "procedures")
|
|
71
|
-
join(l.config.serverDir ?? "server", "api")
|
|
72
|
-
)
|
|
79
|
+
(l) => join(l.config.serverDir ?? "server", "procedures")
|
|
73
80
|
);
|
|
74
81
|
const router = new Router();
|
|
75
82
|
for (const procDir of procedureDirs) {
|
|
@@ -103,11 +110,24 @@ async function generateClientCode(nuxt, createCallerPath) {
|
|
|
103
110
|
const procedures = router[toList]();
|
|
104
111
|
return `
|
|
105
112
|
import { createCaller } from "${withoutExtension(createCallerPath)}";
|
|
106
|
-
|
|
113
|
+
|
|
107
114
|
${procedures.map((p) => `import type ${p.id} from "${withoutExtension(p.file)}";`).join("\n")}
|
|
108
|
-
|
|
115
|
+
|
|
109
116
|
export const apiClient = ${router[toCode]()};
|
|
110
117
|
`;
|
|
111
118
|
}
|
|
119
|
+
async function generateHandlerCode(nuxt, handlerPath) {
|
|
120
|
+
const router = await buildRouterStructure(nuxt);
|
|
121
|
+
const procedures = router[toList]();
|
|
122
|
+
return `
|
|
123
|
+
import { createProceduresHandler } from "${withoutExtension(handlerPath)}";
|
|
124
|
+
|
|
125
|
+
${procedures.map((p) => `import ${p.id} from "${withoutExtension(p.file)}";`).join("\n")}
|
|
126
|
+
|
|
127
|
+
export default createProceduresHandler({
|
|
128
|
+
${procedures.map((p) => `"${p.url.slice(1)}": ${p.id}`).join(",\n ")}
|
|
129
|
+
});
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
112
132
|
|
|
113
133
|
export { module as default };
|
|
@@ -3,7 +3,7 @@ import { useFetch, useRequestHeaders } from "#imports";
|
|
|
3
3
|
export function createCaller(url) {
|
|
4
4
|
return {
|
|
5
5
|
call: (input) => {
|
|
6
|
-
return $fetch("/
|
|
6
|
+
return $fetch("/procedures" + url, {
|
|
7
7
|
method: "POST",
|
|
8
8
|
body: superjson.serialize(input),
|
|
9
9
|
parseResponse: superjson.parse,
|
|
@@ -11,7 +11,7 @@ export function createCaller(url) {
|
|
|
11
11
|
});
|
|
12
12
|
},
|
|
13
13
|
useCall: (input) => {
|
|
14
|
-
return useFetch("/
|
|
14
|
+
return useFetch("/procedures" + url, {
|
|
15
15
|
key: `${url}: ${JSON.stringify(input)}`,
|
|
16
16
|
method: "POST",
|
|
17
17
|
body: superjson.serialize(input),
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createRouter, useBase } from "h3";
|
|
2
|
+
export function createProceduresHandler(procedures) {
|
|
3
|
+
const router = createRouter();
|
|
4
|
+
for (const [path, handler] of Object.entries(procedures)) {
|
|
5
|
+
router.post(`/${path}`, handler);
|
|
6
|
+
}
|
|
7
|
+
return useBase("/procedures", router.handler);
|
|
8
|
+
}
|
package/package.json
CHANGED