@codexsploitx/schemaapi 1.0.0
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/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/bin/schemaapi +302 -0
- package/build.md +246 -0
- package/dist/core/contract.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/schemaapi.cjs.js +13 -0
- package/dist/schemaapi.cjs.js.map +1 -0
- package/dist/schemaapi.esm.js +11 -0
- package/dist/schemaapi.esm.js.map +1 -0
- package/dist/schemaapi.umd.js +19 -0
- package/dist/schemaapi.umd.js.map +1 -0
- package/docs/adapters/deno.md +51 -0
- package/docs/adapters/express.md +67 -0
- package/docs/adapters/fastify.md +64 -0
- package/docs/adapters/hapi.md +67 -0
- package/docs/adapters/koa.md +61 -0
- package/docs/adapters/nest.md +66 -0
- package/docs/adapters/next.md +66 -0
- package/docs/adapters/remix.md +72 -0
- package/docs/cli.md +18 -0
- package/docs/consepts.md +18 -0
- package/docs/getting_started.md +149 -0
- package/docs/sdk.md +25 -0
- package/docs/validation.md +228 -0
- package/docs/versioning.md +28 -0
- package/eslint.config.mjs +34 -0
- package/estructure.md +55 -0
- package/libreria.md +319 -0
- package/package.json +61 -0
- package/readme.md +89 -0
- package/resumen.md +188 -0
- package/rollup.config.js +19 -0
- package/src/adapters/deno.ts +139 -0
- package/src/adapters/express.ts +134 -0
- package/src/adapters/fastify.ts +133 -0
- package/src/adapters/hapi.ts +140 -0
- package/src/adapters/index.ts +9 -0
- package/src/adapters/koa.ts +128 -0
- package/src/adapters/nest.ts +122 -0
- package/src/adapters/next.ts +175 -0
- package/src/adapters/remix.ts +145 -0
- package/src/adapters/ws.ts +132 -0
- package/src/core/client.ts +104 -0
- package/src/core/contract.ts +534 -0
- package/src/core/versioning.test.ts +174 -0
- package/src/docs.ts +535 -0
- package/src/index.ts +5 -0
- package/src/playground.test.ts +98 -0
- package/src/playground.ts +13 -0
- package/src/sdk.ts +17 -0
- package/tests/adapters.deno.test.ts +70 -0
- package/tests/adapters.express.test.ts +67 -0
- package/tests/adapters.fastify.test.ts +63 -0
- package/tests/adapters.hapi.test.ts +66 -0
- package/tests/adapters.koa.test.ts +58 -0
- package/tests/adapters.nest.test.ts +85 -0
- package/tests/adapters.next.test.ts +39 -0
- package/tests/adapters.remix.test.ts +52 -0
- package/tests/adapters.ws.test.ts +91 -0
- package/tests/cli.test.ts +156 -0
- package/tests/client.test.ts +110 -0
- package/tests/contract.handle.test.ts +267 -0
- package/tests/docs.test.ts +96 -0
- package/tests/sdk.test.ts +34 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# SchemaApi - Guía completa de validaciones de Contract
|
|
2
|
+
|
|
3
|
+
> Esta guía sirve como referencia oficial para crear contratos con SchemaApi.
|
|
4
|
+
> Incluye ejemplos de errores, manejo de status codes y buenas prácticas.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1️⃣ Métodos HTTP
|
|
9
|
+
|
|
10
|
+
SchemaApi valida que:
|
|
11
|
+
|
|
12
|
+
- El método (GET, POST, PUT, DELETE, PATCH) esté definido en el contrato.
|
|
13
|
+
- No se pueda llamar a rutas inexistentes o métodos no permitidos.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
"/users/:id": {
|
|
17
|
+
GET: { ... },
|
|
18
|
+
POST: { ... }
|
|
19
|
+
}
|
|
20
|
+
Ejemplo de error:
|
|
21
|
+
|
|
22
|
+
ts
|
|
23
|
+
Copiar código
|
|
24
|
+
// Llamada: DELETE /users/123
|
|
25
|
+
// Contrato no tiene DELETE → SchemaApi lanza error:
|
|
26
|
+
// 405 Method Not Allowed
|
|
27
|
+
2️⃣ Rutas existentes
|
|
28
|
+
Todas las rutas definidas en el contrato son obligatorias.
|
|
29
|
+
|
|
30
|
+
Los parámetros dinámicos deben declararse en params.
|
|
31
|
+
|
|
32
|
+
ts
|
|
33
|
+
Copiar código
|
|
34
|
+
"/posts/:postId/comments/:id": {
|
|
35
|
+
GET: { params: z.object({ postId: z.string(), id: z.string() }) }
|
|
36
|
+
}
|
|
37
|
+
Error típico:
|
|
38
|
+
|
|
39
|
+
ts
|
|
40
|
+
Copiar código
|
|
41
|
+
// Llamada: GET /posts/123/comments
|
|
42
|
+
// Falta param `id` → 400 Bad Request
|
|
43
|
+
3️⃣ Path Params
|
|
44
|
+
Valida tipos y obligatoriedad con Zod.
|
|
45
|
+
|
|
46
|
+
Campos incorrectos → 400 Bad Request
|
|
47
|
+
|
|
48
|
+
ts
|
|
49
|
+
Copiar código
|
|
50
|
+
params: z.object({ id: z.string().uuid() })
|
|
51
|
+
Ejemplo de error:
|
|
52
|
+
|
|
53
|
+
ts
|
|
54
|
+
Copiar código
|
|
55
|
+
GET /users/abc-not-uuid
|
|
56
|
+
// Error: 400 - "Invalid path param 'id'"
|
|
57
|
+
4️⃣ Query Params
|
|
58
|
+
Validación de query strings.
|
|
59
|
+
|
|
60
|
+
Soporta opcionales u obligatorios.
|
|
61
|
+
|
|
62
|
+
ts
|
|
63
|
+
Copiar código
|
|
64
|
+
query: z.object({
|
|
65
|
+
page: z.number().optional(),
|
|
66
|
+
limit: z.number().optional()
|
|
67
|
+
})
|
|
68
|
+
Error típico:
|
|
69
|
+
|
|
70
|
+
ts
|
|
71
|
+
Copiar código
|
|
72
|
+
GET /posts?page=one
|
|
73
|
+
// Error: 400 - "Query param 'page' must be a number"
|
|
74
|
+
5️⃣ Body
|
|
75
|
+
Valida requests POST, PUT, PATCH.
|
|
76
|
+
|
|
77
|
+
Campos faltantes o tipo incorrecto → 400.
|
|
78
|
+
|
|
79
|
+
ts
|
|
80
|
+
Copiar código
|
|
81
|
+
body: z.object({
|
|
82
|
+
username: z.string().min(3),
|
|
83
|
+
email: z.string().email()
|
|
84
|
+
})
|
|
85
|
+
Errores posibles:
|
|
86
|
+
|
|
87
|
+
ts
|
|
88
|
+
Copiar código
|
|
89
|
+
POST /users
|
|
90
|
+
body: { username: "ab" }
|
|
91
|
+
// Error: 400 - "Field 'username' must have at least 3 characters"
|
|
92
|
+
// Error: 400 - "Field 'email' is required"
|
|
93
|
+
6️⃣ Headers
|
|
94
|
+
Valida headers obligatorios y formato.
|
|
95
|
+
|
|
96
|
+
ts
|
|
97
|
+
Copiar código
|
|
98
|
+
headers: z.object({ authorization: z.string() })
|
|
99
|
+
Errores:
|
|
100
|
+
|
|
101
|
+
ts
|
|
102
|
+
Copiar código
|
|
103
|
+
// Falta header Authorization
|
|
104
|
+
// Error: 401 Unauthorized
|
|
105
|
+
7️⃣ Roles y autenticación
|
|
106
|
+
Control de permisos por rol.
|
|
107
|
+
|
|
108
|
+
Roles faltantes → 403 Forbidden
|
|
109
|
+
|
|
110
|
+
ts
|
|
111
|
+
Copiar código
|
|
112
|
+
roles: ["user", "admin"]
|
|
113
|
+
Ejemplo de error:
|
|
114
|
+
|
|
115
|
+
ts
|
|
116
|
+
Copiar código
|
|
117
|
+
// Usuario con rol 'guest' intenta GET /users/123
|
|
118
|
+
// Error: 403 Forbidden
|
|
119
|
+
8️⃣ Responses
|
|
120
|
+
Respuesta del handler debe cumplir el schema.
|
|
121
|
+
|
|
122
|
+
Campos faltantes o adicionales → error en dev/test.
|
|
123
|
+
|
|
124
|
+
Esto asegura consistencia con SDK y docs.
|
|
125
|
+
|
|
126
|
+
ts
|
|
127
|
+
Copiar código
|
|
128
|
+
response: z.object({
|
|
129
|
+
id: z.string(),
|
|
130
|
+
username: z.string(),
|
|
131
|
+
email: z.string().email()
|
|
132
|
+
})
|
|
133
|
+
Ejemplo de error:
|
|
134
|
+
|
|
135
|
+
ts
|
|
136
|
+
Copiar código
|
|
137
|
+
return { id: "123", username: "ferum" }
|
|
138
|
+
// Faltó 'email' → SchemaApi lo detecta
|
|
139
|
+
9️⃣ Status Codes y errores
|
|
140
|
+
Todos los errores posibles se definen en errors.
|
|
141
|
+
|
|
142
|
+
Si un handler devuelve un status code no definido → error.
|
|
143
|
+
|
|
144
|
+
ts
|
|
145
|
+
Copiar código
|
|
146
|
+
errors: {
|
|
147
|
+
401: "UNAUTHORIZED",
|
|
148
|
+
404: "USER_NOT_FOUND",
|
|
149
|
+
500: "INTERNAL_ERROR"
|
|
150
|
+
}
|
|
151
|
+
Ejemplo de error:
|
|
152
|
+
|
|
153
|
+
ts
|
|
154
|
+
Copiar código
|
|
155
|
+
// Handler retorna 418
|
|
156
|
+
// Error: 418 is not defined in contract.errors
|
|
157
|
+
🔟 Consistencia general y breaking changes
|
|
158
|
+
Detecta métodos duplicados, campos obligatorios sin schema.
|
|
159
|
+
|
|
160
|
+
Versionado de contratos detecta breaking changes automáticamente.
|
|
161
|
+
|
|
162
|
+
ts
|
|
163
|
+
Copiar código
|
|
164
|
+
const v1 = createContract({ "/users": { GET: { response: UserSchemaV1 } } });
|
|
165
|
+
const v2 = createContract({ "/users": { GET: { response: UserSchemaV2 } } });
|
|
166
|
+
v2.compareWith(v1); // Detecta cambios incompatibles
|
|
167
|
+
1️⃣1️⃣ Ejemplo completo de contrato con todo
|
|
168
|
+
ts
|
|
169
|
+
Copiar código
|
|
170
|
+
import { createContract } from "schemaapi";
|
|
171
|
+
import { z } from "zod";
|
|
172
|
+
|
|
173
|
+
export const contract = createContract({
|
|
174
|
+
"/users/:id": {
|
|
175
|
+
GET: {
|
|
176
|
+
params: z.object({ id: z.string().uuid() }),
|
|
177
|
+
query: z.object({ includePosts: z.boolean().optional() }),
|
|
178
|
+
headers: z.object({ authorization: z.string() }),
|
|
179
|
+
roles: ["user", "admin"],
|
|
180
|
+
response: z.object({
|
|
181
|
+
id: z.string(),
|
|
182
|
+
username: z.string(),
|
|
183
|
+
email: z.string().email(),
|
|
184
|
+
}),
|
|
185
|
+
errors: {
|
|
186
|
+
401: "UNAUTHORIZED",
|
|
187
|
+
404: "USER_NOT_FOUND"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
PUT: {
|
|
192
|
+
params: z.object({ id: z.string().uuid() }),
|
|
193
|
+
body: z.object({ username: z.string().min(3) }),
|
|
194
|
+
roles: ["admin"],
|
|
195
|
+
response: z.object({ success: z.literal(true) }),
|
|
196
|
+
errors: {
|
|
197
|
+
401: "UNAUTHORIZED",
|
|
198
|
+
403: "FORBIDDEN"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
✅ Resumen de validaciones
|
|
204
|
+
Capa Validación
|
|
205
|
+
Método HTTP ✔
|
|
206
|
+
Ruta existente ✔
|
|
207
|
+
Path params ✔
|
|
208
|
+
Query params ✔
|
|
209
|
+
Body ✔
|
|
210
|
+
Headers ✔
|
|
211
|
+
Roles / permisos ✔
|
|
212
|
+
Status code ✔
|
|
213
|
+
Forma de la response ✔
|
|
214
|
+
Errores declarados ✔
|
|
215
|
+
Consistencia general ✔
|
|
216
|
+
Versionado / breaking ✔
|
|
217
|
+
|
|
218
|
+
💡 Buenas prácticas
|
|
219
|
+
Siempre define roles aunque sea público.
|
|
220
|
+
|
|
221
|
+
Usa Zod para todos params, queries y body.
|
|
222
|
+
|
|
223
|
+
Declara todos los errores posibles.
|
|
224
|
+
|
|
225
|
+
Versiona contratos antes de cambios que rompan compatibilidad.
|
|
226
|
+
|
|
227
|
+
Integra validación en CI/CD para prevenir errores en producción.
|
|
228
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Versionado de SchemaApi
|
|
2
|
+
|
|
3
|
+
SchemaApi permite definir versiones de contratos y detectar breaking changes automáticamente.
|
|
4
|
+
|
|
5
|
+
Ejemplo
|
|
6
|
+
const v1 = createContract({ "/users": { GET: { response: UserSchemaV1 } } });
|
|
7
|
+
const v2 = createContract({ "/users": { GET: { response: UserSchemaV2 } } });
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
v2.compareWith(v1); // Detecta cambios incompatibles
|
|
11
|
+
|
|
12
|
+
Qué detecta
|
|
13
|
+
|
|
14
|
+
Campos eliminados
|
|
15
|
+
|
|
16
|
+
Cambios de tipo
|
|
17
|
+
|
|
18
|
+
Cambios de status code
|
|
19
|
+
|
|
20
|
+
Eliminación de rutas o métodos
|
|
21
|
+
|
|
22
|
+
Beneficios
|
|
23
|
+
|
|
24
|
+
Mantener compatibilidad entre clientes y versiones
|
|
25
|
+
|
|
26
|
+
Detectar errores antes de desplegar
|
|
27
|
+
|
|
28
|
+
Facilita migración entre versiones
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
import pluginJs from "@eslint/js";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
import eslintConfigPrettier from "eslint-config-prettier";
|
|
5
|
+
|
|
6
|
+
export default tseslint.config(
|
|
7
|
+
{
|
|
8
|
+
ignores: ["dist/**", "node_modules/**", "coverage/**"],
|
|
9
|
+
},
|
|
10
|
+
pluginJs.configs.recommended,
|
|
11
|
+
...tseslint.configs.recommended,
|
|
12
|
+
{
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: { ...globals.node, ...globals.browser },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
files: ["**/*.ts", "**/*.tsx"],
|
|
19
|
+
languageOptions: {
|
|
20
|
+
parserOptions: {
|
|
21
|
+
project: true,
|
|
22
|
+
tsconfigRootDir: import.meta.dirname,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
files: ["**/*.{js,mjs,cjs}"],
|
|
28
|
+
...tseslint.configs.disableTypeChecked,
|
|
29
|
+
rules: {
|
|
30
|
+
"@typescript-eslint/no-require-imports": "off",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
eslintConfigPrettier
|
|
34
|
+
);
|
package/estructure.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Estructura Recomendada
|
|
2
|
+
|
|
3
|
+
schemaapi/
|
|
4
|
+
├── src/
|
|
5
|
+
│ ├── core/
|
|
6
|
+
│ │ ├── contract.ts # createContract y definición de contratos
|
|
7
|
+
│ │ ├── validator.ts # Validación de requests/responses
|
|
8
|
+
│ │ ├── context.ts # Contexto de request (params, body, headers)
|
|
9
|
+
│ │ └── response-validator.ts # Validación de respuestas
|
|
10
|
+
│ ├── adapters/
|
|
11
|
+
│ │ ├── express.ts
|
|
12
|
+
│ │ ├── next.ts
|
|
13
|
+
│ │ ├── fastify.ts
|
|
14
|
+
│ │ └── hono.ts
|
|
15
|
+
│ ├── generators/
|
|
16
|
+
│ │ ├── docs.ts # Generación de documentación
|
|
17
|
+
│ │ ├── sdk.ts # Generación de SDK tipado
|
|
18
|
+
│ │ └── tests.ts # Generación de tests automáticos
|
|
19
|
+
│ └── cli/
|
|
20
|
+
│ ├── index.ts
|
|
21
|
+
│ └── commands/
|
|
22
|
+
├── types/
|
|
23
|
+
│ └── index.d.ts # Tipos globales para JS
|
|
24
|
+
├── docs/
|
|
25
|
+
│ ├── concepts.md
|
|
26
|
+
│ ├── validation.md
|
|
27
|
+
│ ├── cli.md
|
|
28
|
+
│ ├── sdk.md
|
|
29
|
+
│ └── versioning.md
|
|
30
|
+
├── examples/
|
|
31
|
+
│ ├── express-example.ts
|
|
32
|
+
│ ├── nextjs-example.ts
|
|
33
|
+
│ └── client-js.ts
|
|
34
|
+
├── package.json
|
|
35
|
+
├── tsconfig.json
|
|
36
|
+
├── README.md
|
|
37
|
+
└── LICENSE
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
🏗️ Estructura interna
|
|
41
|
+
|
|
42
|
+
core/ → lógica principal de contratos y validaciones
|
|
43
|
+
|
|
44
|
+
adapters/ → integración con frameworks
|
|
45
|
+
|
|
46
|
+
generators/ → docs, SDK y tests automáticos
|
|
47
|
+
|
|
48
|
+
cli/ → herramientas de línea de comandos
|
|
49
|
+
|
|
50
|
+
types/ → tipos para JS
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
|
package/libreria.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
Plan para crear la librería schemaapi en npm
|
|
2
|
+
1️⃣ Stack y herramientas
|
|
3
|
+
|
|
4
|
+
Lenguaje: TypeScript (permite generar JS + tipado para usuarios TS)
|
|
5
|
+
|
|
6
|
+
Bundler: Rollup (para crear el bundle de npm, compatible con Node y Browser)
|
|
7
|
+
|
|
8
|
+
Target: Node.js y Browser (UMD + ESM)
|
|
9
|
+
|
|
10
|
+
Testing: Vitest o Jest
|
|
11
|
+
|
|
12
|
+
Lint / Formato: ESLint + Prettier
|
|
13
|
+
|
|
14
|
+
Publicación: npm
|
|
15
|
+
|
|
16
|
+
2️⃣ Estructura del proyecto
|
|
17
|
+
schemaapi/
|
|
18
|
+
├─ src/
|
|
19
|
+
│ ├─ index.ts # Entry point de la librería
|
|
20
|
+
│ ├─ contract.ts # createContract
|
|
21
|
+
│ ├─ sdk.ts # generateSDK
|
|
22
|
+
│ ├─ adapters/ # Adaptadores para Express, Next, Fastify...
|
|
23
|
+
│ │ ├─ express.ts
|
|
24
|
+
│ │ ├─ next.ts
|
|
25
|
+
│ │ └─ fastify.ts
|
|
26
|
+
│ └─ utils/ # Helpers internos
|
|
27
|
+
├─ package.json
|
|
28
|
+
├─ tsconfig.json
|
|
29
|
+
├─ rollup.config.ts
|
|
30
|
+
├─ README.md
|
|
31
|
+
└─ LICENSE
|
|
32
|
+
|
|
33
|
+
3️⃣ Funcionalidades que debe exponer la librería
|
|
34
|
+
// src/index.ts
|
|
35
|
+
export { createContract } from "./contract";
|
|
36
|
+
export { generateSDK } from "./sdk";
|
|
37
|
+
|
|
38
|
+
// Adapters (opcional)
|
|
39
|
+
export * as adapters from "./adapters";
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
createContract → define los endpoints y validaciones (params, query, body, headers, roles, response, errores)
|
|
43
|
+
|
|
44
|
+
generateSDK → genera un SDK tipado listo para usar
|
|
45
|
+
|
|
46
|
+
adapters → integración rápida con frameworks populares (Express, Next.js, Fastify, NestJS, etc.)
|
|
47
|
+
|
|
48
|
+
4️⃣ Ejemplo de uso después de publicar en npm
|
|
49
|
+
// Instalación
|
|
50
|
+
npm install schemaapi
|
|
51
|
+
|
|
52
|
+
// Definir contrato
|
|
53
|
+
import { createContract, generateSDK, adapters } from "schemaapi";
|
|
54
|
+
import { z } from "zod";
|
|
55
|
+
|
|
56
|
+
const userContract = createContract({
|
|
57
|
+
"/users/:id": {
|
|
58
|
+
GET: {
|
|
59
|
+
params: z.object({ id: z.string().uuid() }),
|
|
60
|
+
headers: z.object({ authorization: z.string() }),
|
|
61
|
+
roles: ["user", "admin"],
|
|
62
|
+
response: z.object({
|
|
63
|
+
id: z.string(),
|
|
64
|
+
username: z.string(),
|
|
65
|
+
email: z.string().email(),
|
|
66
|
+
}),
|
|
67
|
+
errors: {
|
|
68
|
+
401: "UNAUTHORIZED",
|
|
69
|
+
404: "USER_NOT_FOUND",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Generar SDK listo para usar
|
|
76
|
+
const schemaApiSdk = generateSDK(userContract, { baseUrl: "http://localhost:3000" });
|
|
77
|
+
|
|
78
|
+
// Usar en Express
|
|
79
|
+
import express from "express";
|
|
80
|
+
const app = express();
|
|
81
|
+
app.use(express.json());
|
|
82
|
+
adapters.express.handleContract(app, userContract);
|
|
83
|
+
app.listen(3000);
|
|
84
|
+
|
|
85
|
+
// Usar SDK
|
|
86
|
+
const user = await schemaApiSdk.getUser({ params: { id: "uuid-aqui" }, headers: { authorization: "Bearer TOKEN" } });
|
|
87
|
+
|
|
88
|
+
5️⃣ Configuración de Rollup
|
|
89
|
+
// rollup.config.ts
|
|
90
|
+
import typescript from "@rollup/plugin-typescript";
|
|
91
|
+
|
|
92
|
+
export default {
|
|
93
|
+
input: "src/index.ts",
|
|
94
|
+
output: [
|
|
95
|
+
{ file: "dist/schemaapi.cjs.js", format: "cjs" },
|
|
96
|
+
{ file: "dist/schemaapi.esm.js", format: "esm" },
|
|
97
|
+
],
|
|
98
|
+
plugins: [typescript({ tsconfig: "./tsconfig.json" })],
|
|
99
|
+
external: ["zod"], // No incluir dependencias externas
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
Esto permite que tu librería sea usable tanto en Node como en Browser
|
|
104
|
+
|
|
105
|
+
Usuarios solo hacen import { createContract } from "schemaapi" y listo
|
|
106
|
+
|
|
107
|
+
6️⃣ Publicación en npm
|
|
108
|
+
npm login
|
|
109
|
+
npm version patch
|
|
110
|
+
npm publish --access public
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
Cada cambio mayor: npm version major
|
|
114
|
+
|
|
115
|
+
Cada cambio menor: npm version minor
|
|
116
|
+
|
|
117
|
+
Cada bugfix: npm version patch
|
|
118
|
+
|
|
119
|
+
7️⃣ Bonus: integración lista para IDE + IA
|
|
120
|
+
|
|
121
|
+
Todos los métodos de SDK son tipados → autocompletado inmediato
|
|
122
|
+
|
|
123
|
+
Cada endpoint tiene params/query/body/headers tipados desde el contrato
|
|
124
|
+
|
|
125
|
+
Adaptadores permiten integración rápida en Express, Next.js, Fastify, NestJS, etc.
|
|
126
|
+
|
|
127
|
+
Validación de response y errores en tiempo de desarrollo
|
|
128
|
+
|
|
129
|
+
Si quieres, puedo hacer un prototipo completo de librería lista para publicar en npm, con:
|
|
130
|
+
|
|
131
|
+
createContract + validaciones completas
|
|
132
|
+
|
|
133
|
+
generateSDK tipado automáticamente
|
|
134
|
+
|
|
135
|
+
Adaptadores Express / Next / Fastify
|
|
136
|
+
|
|
137
|
+
Ejemplos de uso listos para instalar
|
|
138
|
+
|
|
139
|
+
Rollup + TS configurado para npm
|
|
140
|
+
|
|
141
|
+
Esto sería básicamente tu librería lista para usar en producción.
|
|
142
|
+
|
|
143
|
+
¿Quieres que haga eso ahora?
|
|
144
|
+
|
|
145
|
+
dame el .md para que mi ide trae ia lo haga con todos los adapatores y compatibilidad
|
|
146
|
+
|
|
147
|
+
¡Perfecto! 😎 Te voy a armar un README.md completo que tu IDE con IA pueda usar para autocompletar y generar todo: contratos, SDK, y adaptadores listos para todos los frameworks populares. Todo explicado paso a paso y con ejemplos.
|
|
148
|
+
|
|
149
|
+
# SchemaApi - Librería NPM: Contratos, SDK y Adaptadores
|
|
150
|
+
|
|
151
|
+
> Guía completa de uso de SchemaApi con todos los adaptadores y compatibilidad para frameworks populares.
|
|
152
|
+
> Diseñado para autocompletado y soporte de IDEs con IA.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 1️⃣ Instalación
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm install schemaapi zod
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
zod es la dependencia principal para la validación de schemas.
|
|
163
|
+
|
|
164
|
+
2️⃣ Importación principal
|
|
165
|
+
import { createContract, generateSDK, adapters } from "schemaapi";
|
|
166
|
+
import { z } from "zod";
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
createContract: define tus endpoints y validaciones
|
|
170
|
+
|
|
171
|
+
generateSDK: genera un SDK tipado listo para usar
|
|
172
|
+
|
|
173
|
+
adapters: integración rápida con frameworks populares
|
|
174
|
+
|
|
175
|
+
3️⃣ Crear un contrato de ejemplo
|
|
176
|
+
export const userContract = createContract({
|
|
177
|
+
"/users/:id": {
|
|
178
|
+
GET: {
|
|
179
|
+
params: z.object({ id: z.string().uuid() }),
|
|
180
|
+
query: z.object({ includePosts: z.boolean().optional() }),
|
|
181
|
+
headers: z.object({ authorization: z.string() }),
|
|
182
|
+
roles: ["user", "admin"],
|
|
183
|
+
response: z.object({
|
|
184
|
+
id: z.string(),
|
|
185
|
+
username: z.string(),
|
|
186
|
+
email: z.string().email(),
|
|
187
|
+
}),
|
|
188
|
+
errors: {
|
|
189
|
+
401: "UNAUTHORIZED",
|
|
190
|
+
404: "USER_NOT_FOUND",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
PUT: {
|
|
194
|
+
params: z.object({ id: z.string().uuid() }),
|
|
195
|
+
body: z.object({ username: z.string().min(3) }),
|
|
196
|
+
roles: ["admin"],
|
|
197
|
+
response: z.object({ success: z.literal(true) }),
|
|
198
|
+
errors: {
|
|
199
|
+
401: "UNAUTHORIZED",
|
|
200
|
+
403: "FORBIDDEN",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
4️⃣ Generar el SDK
|
|
207
|
+
export const schemaApiSdk = generateSDK(userContract, {
|
|
208
|
+
baseUrl: "http://localhost:3000",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
Todos los endpoints del contrato se convierten en métodos tipados
|
|
213
|
+
|
|
214
|
+
Incluye validación automática de params, query, body, headers y response
|
|
215
|
+
|
|
216
|
+
Ejemplo de uso del SDK:
|
|
217
|
+
|
|
218
|
+
const user = await schemaApiSdk.getUser({
|
|
219
|
+
params: { id: "uuid-aqui" },
|
|
220
|
+
query: { includePosts: true },
|
|
221
|
+
headers: { authorization: "Bearer TOKEN" },
|
|
222
|
+
});
|
|
223
|
+
console.log(user.username);
|
|
224
|
+
|
|
225
|
+
5️⃣ Adaptadores para frameworks populares
|
|
226
|
+
5.1 Express
|
|
227
|
+
import express from "express";
|
|
228
|
+
const app = express();
|
|
229
|
+
app.use(express.json());
|
|
230
|
+
adapters.express.handleContract(app, userContract);
|
|
231
|
+
app.listen(3000);
|
|
232
|
+
|
|
233
|
+
5.2 Next.js API Routes
|
|
234
|
+
// pages/api/users/[id].ts
|
|
235
|
+
export default adapters.next.handleContract(userContract);
|
|
236
|
+
|
|
237
|
+
5.3 Fastify
|
|
238
|
+
import Fastify from "fastify";
|
|
239
|
+
const fastify = Fastify({ logger: true });
|
|
240
|
+
adapters.fastify.handleContract(fastify, userContract);
|
|
241
|
+
fastify.listen({ port: 3000 });
|
|
242
|
+
|
|
243
|
+
5.4 NestJS
|
|
244
|
+
import { Module } from "@nestjs/common";
|
|
245
|
+
|
|
246
|
+
@Module({
|
|
247
|
+
imports: [
|
|
248
|
+
adapters.nest.SchemaApiModule.register({ contracts: [userContract] }),
|
|
249
|
+
],
|
|
250
|
+
})
|
|
251
|
+
export class AppModule {}
|
|
252
|
+
|
|
253
|
+
5.5 Koa
|
|
254
|
+
import Koa from "koa";
|
|
255
|
+
import bodyParser from "koa-bodyparser";
|
|
256
|
+
const app = new Koa();
|
|
257
|
+
app.use(bodyParser());
|
|
258
|
+
adapters.koa.handleContract(app, userContract);
|
|
259
|
+
app.listen(3000);
|
|
260
|
+
|
|
261
|
+
5.6 Hapi
|
|
262
|
+
import Hapi from "@hapi/hapi";
|
|
263
|
+
const server = Hapi.server({ port: 3000 });
|
|
264
|
+
await adapters.hapi.handleContract(server, userContract);
|
|
265
|
+
await server.start();
|
|
266
|
+
|
|
267
|
+
5.7 Remix
|
|
268
|
+
// app/routes/users.$id.ts
|
|
269
|
+
export const loader = adapters.remix.handleContract(userContract);
|
|
270
|
+
export const action = adapters.remix.handleContract(userContract);
|
|
271
|
+
|
|
272
|
+
5.8 Bun / Deno
|
|
273
|
+
import { serve } from "bun";
|
|
274
|
+
serve(adapters.deno.handleContract(userContract), { port: 3000 });
|
|
275
|
+
|
|
276
|
+
6️⃣ Ejemplo completo de frontend usando SDK
|
|
277
|
+
import { schemaApiSdk } from "../sdk";
|
|
278
|
+
|
|
279
|
+
async function loadUser(id: string) {
|
|
280
|
+
try {
|
|
281
|
+
const user = await schemaApiSdk.getUser({
|
|
282
|
+
params: { id },
|
|
283
|
+
query: { includePosts: true },
|
|
284
|
+
headers: { authorization: "Bearer TOKEN" },
|
|
285
|
+
});
|
|
286
|
+
console.log(user.username, user.email);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
console.error("Error:", err.message);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
Compatible con React, Next.js o cualquier frontend
|
|
294
|
+
|
|
295
|
+
Tipado y autocompletado garantizado
|
|
296
|
+
|
|
297
|
+
7️⃣ Beneficios
|
|
298
|
+
|
|
299
|
+
Una sola fuente de verdad (contract) para todos los frameworks
|
|
300
|
+
|
|
301
|
+
Validación completa: params, query, body, headers, roles, response, status codes
|
|
302
|
+
|
|
303
|
+
SDK tipado listo para backend y frontend
|
|
304
|
+
|
|
305
|
+
Compatible con IDEs IA → autocompletado y prevención de errores
|
|
306
|
+
|
|
307
|
+
Fácil integración con Express, Next.js, Fastify, NestJS, Koa, Hapi, Remix, Bun, Deno
|
|
308
|
+
|
|
309
|
+
8️⃣ Tips para IDE con IA
|
|
310
|
+
|
|
311
|
+
Mantener todos los contratos en un archivo central contracts/index.ts
|
|
312
|
+
|
|
313
|
+
Siempre usar TypeScript para tipado completo
|
|
314
|
+
|
|
315
|
+
Documentar cada endpoint para que la IA sugiera tipos y errores automáticamente
|
|
316
|
+
|
|
317
|
+
Usar schemaApiSdk para llamadas reales en frontend o backend
|
|
318
|
+
|
|
319
|
+
Cambios en el contrato → regenerar SDK → autocompletado actualizado
|