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 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: ['nuxt-procedures'],
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/api` directory. The module will automatically create a corresponding client for each file.
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/api/hello.ts`:
65
+ `server/procedures/hello.ts`:
66
+
64
67
  ```typescript
65
- import { z } from 'zod'
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/api/users/create.ts`:
83
+ `server/procedures/users/create.ts`:
84
+
81
85
  ```typescript
82
- import { z } from 'zod'
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(['admin', 'user']).default('user'),
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/api` directory.
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('World')
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 and is useful for calling procedures from server-side code or when you don't need the features of `useFetch`.
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('World')
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: 'Andres',
158
- email: 'andres@example.com',
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-procedures",
3
3
  "configKey": "procedures",
4
- "version": "0.1.5",
4
+ "version": "0.1.6",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.0"
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("api")) {
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("/api" + url, {
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("/api" + url, {
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,2 @@
1
+ import type { EventHandler } from 'h3';
2
+ export declare function createProceduresHandler(procedures: Record<string, EventHandler>): EventHandler;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-procedures",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Nuxt module to define and easily consume your backend API in a validated and type-safe way using procedures and Zod schemas.",
5
5
  "keywords": [
6
6
  "nuxt",