Skip to main content

Code Generation

@xond/api generates type-safe REST services, controllers, and frontend models from your Prisma schema. This section explains how the generation process works and what gets created.

Overview

The code generator reads your prisma/schema.prisma file, parses all models and their relationships, and generates:

  • Backend REST services – Full CRUD operations with filtering, pagination, and sorting
  • Frontend TypeScript models – Type definitions and column metadata for UI components
  • Service registry – Centralized registry for accessing all services
  • Type mappings – Field type maps for form generation

Generated Files Structure

After running xond-api generate, your project structure will look like this:

your-api-app/
├── src/
│ └── modules/
│ └── json/
│ └── services/
│ └── generated/
│ ├── userService.ts
│ ├── productService.ts
│ ├── serviceRegistry.ts
│ └── fieldTypeMap.ts
└── your-ui-app/
└── src/
└── generated/
└── models/
├── userModel.tsx
├── productModel.tsx
└── index.ts

Backend Services

Each model in your Prisma schema generates a corresponding service file.

Service File Structure

// src/modules/json/services/generated/userService.ts
import { PrismaClient } from "@prisma/client";
import { normalizeArrayParam } from "@xond/api/lib/util";

export const getUser = async (prisma: PrismaClient, id: string) => {
// Implementation
};

export const getUsers = async (
prisma: PrismaClient,
params: {
page?: number;
pageSize?: number;
sortBy?: string;
sortOrder?: "asc" | "desc";
filters?: Record<string, any>;
}
) => {
// Implementation with pagination, sorting, filtering
};

export const createUser = async (prisma: PrismaClient, data: any) => {
// Implementation
};

export const updateUser = async (prisma: PrismaClient, id: string, data: any) => {
// Implementation
};

export const deleteUser = async (prisma: PrismaClient, id: string) => {
// Implementation
};

Service Features

Each generated service includes:

  • CRUD operations – Create, Read, Update, Delete
  • Paginationpage and pageSize parameters
  • SortingsortBy and sortOrder parameters
  • Filtering – Dynamic filter building from query parameters
  • Relations – Automatic handling of Prisma relations
  • Type safety – Full TypeScript type inference

Service Registry

All services are registered in serviceRegistry.ts:

import * as userService from "./userService";
import * as productService from "./productService";

export const serviceRegistry = {
"user": userService,
"product": productService,
};

This allows dynamic service access:

import { serviceRegistry } from "./generated/serviceRegistry";

const userService = serviceRegistry["user"];
const users = await userService.getUsers(prisma, { page: 1, pageSize: 10 });

Frontend Models

Frontend models provide TypeScript types and metadata for UI components.

Model File Structure

// src/generated/models/userModel.tsx
export interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}

export interface UserColumnModel {
id: { label: string; type: string; required: boolean };
name: { label: string; type: string; required: boolean };
email: { label: string; type: string; required: boolean };
createdAt: { label: string; type: string; required: boolean };
}

export const UserModel: UserColumnModel = {
id: { label: "ID", type: "uuid", required: true },
name: { label: "Name", type: "string", required: true },
email: { label: "Email", type: "string", required: true },
createdAt: { label: "Created At", type: "datetime", required: false },
};

export const UserColumnModel: UserColumnModel = UserModel;

Model Registry

Models are exported and registered in index.ts:

export * from "./userModel";
export * from "./productModel";

export const modelRegistry = {
user: UserModel,
product: ProductModel,
};

export const columnModelRegistry = {
user: UserColumnModel,
product: ProductColumnModel,
};

Field Type Mapping

The generator creates fieldTypeMap.ts which maps Prisma field types to form input types:

export const fieldTypeMap = {
user: {
id: "uuid",
name: "text",
email: "email",
createdAt: "datetime",
},
product: {
id: "uuid",
name: "text",
price: "money",
quantity: "number",
},
};

This is used by @xond/ui components to automatically render the correct input types.

Customization

Skipping Models

You can skip certain models from generation by configuring skipTables in src/config/customConfig.ts:

export const customConfig = {
skipTables: ["_Migration", "AuditLog"],
};

Model Overrides

You can customize how models are parsed by providing an override function:

// src/config/customConfig.ts
export const overrideModel = (models: Model[]) => {
// Customize models before generation
return models.map(model => {
if (model.name === "User") {
// Add custom fields or modify structure
}
return model;
});
};

Regeneration

You can safely regenerate code multiple times. The generator:

  • Overwrites generated service files
  • Preserves custom code in separate files
  • Updates registries and indexes automatically

Best practice: Keep custom business logic in separate files, not in generated files.

Integration with Express

Generated services are designed to work with Express routes:

import { serviceRegistry } from "./modules/json/services/generated/serviceRegistry";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

app.get("/api/users", async (req, res) => {
const users = await serviceRegistry["user"].getUsers(prisma, {
page: parseInt(req.query.page as string) || 1,
pageSize: parseInt(req.query.pageSize as string) || 10,
sortBy: req.query.sortBy as string,
sortOrder: req.query.sortOrder as "asc" | "desc",
});
res.json(users);
});

Next Steps