Skip to content

CLI Guide

Veloce-TS ships with a built-in CLI for project creation and code generation. Every generated file passes bun run typecheck out of the box.

The veloce and veloce-ts binaries are available after installing the package:

Terminal window
bun add veloce-ts
# or globally
bun add -g veloce-ts

Scaffolds a complete project.

Terminal window
veloce new my-api

Generated structure:

my-api/
├── src/
│ ├── index.ts # app entry with usePlugin calls
│ ├── app.module.ts # app-level barrel
│ └── modules/
│ └── health/
│ ├── health.controller.ts
│ └── health.module.ts
├── tests/
│ └── health.test.ts
├── package.json # veloce-ts dep, bun scripts
├── tsconfig.json # Stage-2 decorators + reflect-metadata
└── .env.example

Templates:

Terminal window
veloce new my-api # REST (default)
veloce new my-api --template rest
veloce new my-api --template fullstack # includes GraphQL + WebSocket plugins

Generate individual pieces of your application. All commands accept a <name> argument.

Generates a full module folder: controller + service + DTO + barrel.

Terminal window
veloce generate module users

Output in src/modules/users/:

users.controller.ts # @Controller('/users') with CRUD stubs
users.service.ts # @Injectable() service
users.dto.ts # CreateUsersDto, UpdateUsersDto (Zod)
users.module.ts # barrel: re-exports controller + service
Terminal window
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 };
}
}
Terminal window
veloce generate service products
src/services/products.service.ts
import { Injectable } from 'veloce-ts';
@Injectable()
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 };
}
}
Terminal window
veloce generate dto create-product
src/dtos/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>;
Terminal window
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 };
}
}
Terminal window
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: 'Unauthorized' }, 401);
}
await next();
}
Terminal window
veloce generate plugin analytics
src/plugins/analytics.plugin.ts
import type { VelocePlugin, VeloceTS } from 'veloce-ts';
export class AnalyticsPlugin implements VelocePlugin {
name = 'analytics';
async register(app: VeloceTS): Promise<void> {
// register routes or middleware here
}
}
CommandOutput path
generate module <name>src/modules/<name>/
generate controller <name>src/controllers/<name>.controller.ts
generate service <name>src/services/<name>.service.ts
generate dto <name>src/dtos/<name>.dto.ts
generate resolver <name>src/resolvers/<name>.resolver.ts
generate middleware <name>src/middleware/<name>.middleware.ts
generate plugin <name>src/plugins/<name>.plugin.ts

The CLI detects an existing src/ directory and places files there. If src/ is not found, files are placed in the current directory.

The <name> argument is normalized automatically:

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