Saltearse al contenido

Guía CLI

Veloce-TS incluye una CLI integrada para crear proyectos y generar código. Todos los archivos generados superan npm run typecheck sin ningún cambio, y desde v3.1.0 todo lo que produce la CLI funciona tanto en Node como en Bun.

Los binarios veloce y veloce-ts están disponibles tras instalar el paquete:

Ventana de terminal
npm install veloce-ts
# o de forma global
npm install -g veloce-ts
# Con Bun es igual
bun add veloce-ts

Genera un proyecto completo.

Ventana de terminal
veloce new mi-api

Estructura generada (template rest; cada uno escribe solo los directorios que llena):

mi-api/
├── src/
│ ├── index.ts # entrada de la app con llamadas usePlugin
│ ├── controllers/
│ │ └── user.controller.ts # inyecta UserService con @Inject
│ └── services/
│ └── user.service.ts # resuelto por el contenedor de DI
├── public/
│ └── docs.html # Swagger UI independiente
├── package.json # los scripts pasan por el binario veloce
├── tsconfig.json # decoradores Stage-2 + reflect-metadata
├── .env.example # cada variable que lee el código generado
├── .gitignore
└── README.md

Los templates graphql y fullstack añaden src/resolvers/, websocket y fullstack añaden src/websockets/, y todos salvo websocket añaden src/services/.

Opciones:

OpciónPor defectoQué hace
-t, --template <template>restrest, graphql, websocket o fullstack
--installoffInstala dependencias (Bun si está disponible, npm si no)
--gitoffgit init más un commit inicial — se salta si el destino ya está dentro de un repositorio

Los ejemplos usan el contenedor de DI. El controlador y el resolver generados piden su UserService con @Inject(UserService) en vez de construirlo, así que reemplazar el array en memoria por una base de datos no toca a ningún llamador. Un scaffolding que construye sus dependencias a mano enseña justo lo contrario de lo que ofrece el framework.

Templates:

Ventana de terminal
veloce new mi-api # REST (por defecto)
veloce new mi-api --template rest
veloce new mi-api --template graphql # resolver GraphQL + playground
veloce new mi-api --template websocket # gateway WebSocket
veloce new mi-api --template fullstack # REST + GraphQL + WebSocket

:::note Los cuatro templates corren en Node Incluidos websocket y fullstack, que antes lanzaban al arrancar ahí. Declaran @hono/node-ws (sin uso bajo Bun y Deno, que hacen el upgrade nativamente). CI genera cada template y lo instala, typechequea, compila y arranca bajo Node en cada ejecución. :::

El package.json generado enruta sus scripts por el binario veloce en vez de fijar un runtime, así que el mismo proyecto funciona de las dos formas:

{
"scripts": {
"dev": "veloce dev",
"build": "veloce build",
"start": "node dist/index.js",
"typecheck": "tsc --noEmit"
}
}

Arranca la app con recarga en caliente.

Ventana de terminal
veloce dev
veloce dev --port 8080
veloce dev --runtime node # fuerza el camino de Node

Con Bun usa bun --watch. Con Node ejecuta tsx, que transpila los decoradores Stage-2 que el type stripping propio de Node no sabe manejar; tsx viene como devDependency en los proyectos generados.

OpciónPor defectoQué hace
-p, --port <port>3000Puerto que se pasa a la app como PORT
-w, --watch <path>srcRutas extra a vigilar
-r, --runtime <runtime>autoauto, bun o node

Compila el proyecto para producción.

Ventana de terminal
veloce build
veloce build --runtime node
veloce build --outdir build

Con Bun empaqueta con Bun.build y emite ESM y CJS. Con Node compila usando el tsc del propio proyecto, respetando tu tsconfig.json, que es lo que emite los metadatos de decoradores que el framework lee.

:::note El camino de Bun exige que la propia CLI corra bajo Bun Bun.build es una API en proceso, así que tener el binario bun en el PATH no basta: npm run build en una máquina con Bun instalado igual toma el camino de Node, porque npm arranca la CLI con Node. Usa bun run build, o pasa --runtime bun (que falla si el proceso no es Bun). :::

OpciónPor defectoQué hace
-o, --outdir <dir>distDirectorio de salida
-m, --minifyoffMinifica (camino Bun)
-s, --sourcemapoffEmite sourcemaps. Desactivado por defecto: publicarlos es una decisión de tamaño de despliegue, y el paquete del propio framework era 73% sourcemaps antes de que esa lección aterrizara en 3.1.2
-f, --format <format>bothesm, cjs o both — solo camino Bun
-r, --runtime <runtime>autoauto, bun o node

Genera piezas individuales de tu aplicación. Todos los comandos aceptan un argumento <nombre>.

Genera una carpeta de módulo completa: controlador + servicio + DTO + barrel.

Ventana de terminal
veloce generate module users

Salida en src/modules/users/:

users.controller.ts # @Controller('/users') con stubs CRUD
users.service.ts # clase de servicio plana
users.dto.ts # CreateUsersDto, UpdateUsersDto (Zod)
users.module.ts # barrel: re-exporta controlador + servicio
Ventana de terminal
veloce generate controller products
src/controllers/products.controller.ts
import { Controller, Get, Post, Put, Delete, Param, Body } from 'veloce-ts';
import { z } from 'zod';
const CreateProductsSchema = z.object({
name: z.string().min(1),
});
@Controller('/products')
export class ProductsController {
@Get('/')
async findAll() {
return [];
}
@Get('/:id')
async findOne(@Param('id') id: string) {
return { id };
}
@Post('/')
async create(@Body(CreateProductsSchema) body: z.infer<typeof CreateProductsSchema>) {
return body;
}
@Put('/:id')
async update(@Param('id') id: string, @Body() body: unknown) {
return { id, ...body as object };
}
@Delete('/:id')
async remove(@Param('id') id: string) {
return { id, deleted: true };
}
}
Ventana de terminal
veloce generate service products
src/services/products.service.ts
export class ProductsService {
async findAll() {
return [];
}
async findOne(id: string) {
return { id };
}
async create(data: unknown) {
return data;
}
async update(id: string, data: unknown) {
return { id, ...data as object };
}
async remove(id: string) {
return { id, deleted: true };
}
}
Ventana de terminal
veloce generate dto create-product
src/dto/create-product.dto.ts
import { z } from 'zod';
export const CreateProductSchema = z.object({
name: z.string().min(1),
});
export type CreateProductDto = z.infer<typeof CreateProductSchema>;
Ventana de terminal
veloce generate resolver user
src/resolvers/user.resolver.ts
import { Resolver, GQLQuery, GQLMutation, Arg } from 'veloce-ts/graphql';
import { z } from 'zod';
@Resolver('user')
export class UserResolver {
@GQLQuery('getUsers')
async getAll() {
return [];
}
@GQLQuery('getUser')
async getOne(@Arg('id', z.string()) id: string) {
return { id };
}
@GQLMutation('createUser')
async create(@Arg('name', z.string()) name: string) {
return { id: Date.now().toString(), name };
}
}
Ventana de terminal
veloce generate middleware auth
src/middleware/auth.middleware.ts
import type { Context, Next } from 'hono';
export async function authMiddleware(c: Context, next: Next) {
const token = c.req.header('authorization');
if (!token) {
return c.json({ error: 'No autorizado' }, 401);
}
await next();
}

Un gateway @WebSocket con los tres hooks de ciclo de vida y un schema Zod de mensajes.

Ventana de terminal
veloce generate gateway presence

Crea src/websockets/presence.websocket.ts con @OnConnect, @OnMessage y @OnDisconnect, uniéndose a una sala con el nombre del gateway. Se registra como un controlador:

app.include(PresenceGateway);
app.usePlugin(new WebSocketPlugin());

Solo un @OnMessage por gateway — la metadata guarda un único manejador de mensajes — así que modela los distintos payloads del cliente como una unión discriminada sobre type.

Una clase de listeners @On.

Ventana de terminal
veloce generate listener audit

Crea src/listeners/audit.listener.ts. Los listeners se registran con app.include() y se resuelven por el contenedor de DI, así que inyectan servicios igual que un controlador. Las suscripciones se enganchan en compile() y se sueltan en shutdown().

Ventana de terminal
veloce generate plugin analytics
src/plugins/analytics.plugin.ts
import type { Plugin, VeloceTS } from 'veloce-ts';
export class AnalyticsPlugin implements Plugin {
name = 'analytics';
async register(app: VeloceTS): Promise<void> {
// registra rutas o middleware aquí
}
}
ComandoRuta de salida
generate module <nombre>src/modules/<nombre>/
generate controller <nombre>src/controllers/<nombre>.controller.ts
generate service <nombre>src/services/<nombre>.service.ts
generate dto <nombre>src/dto/<nombre>.dto.ts
generate resolver <nombre>src/resolvers/<nombre>.resolver.ts
generate middleware <nombre>src/middleware/<nombre>.middleware.ts
generate plugin <nombre>src/plugins/<nombre>.plugin.ts
generate gateway <nombre>src/websockets/<nombre>.websocket.ts
generate listener <nombre>src/listeners/<nombre>.listener.ts

La CLI detecta un directorio src/ existente y coloca los archivos allí. Si no se encuentra src/, los archivos se colocan en el directorio actual.

El argumento <nombre> se normaliza automáticamente:

EntradaPascalCasekebab-case
userUseruser
UserProfileUserProfileuser-profile
create-productCreateProductcreate-product