Quick Overview
Hey, let me tell you about this cool thing I did. I spotted all those boring, repeating bits in my code and whipped up a custom generator to handle them automatically. The result? Everything stays uniform, it's a breeze to update, and I get stuff done way quicker. Plus, I brought in some AI smarts to keep the tool itself in top shape without much hassle.
Kicking Off the Change: Ditching Manual Typing for Good
Working as a backend dev, I kept running into the same old hassle of churning out identical code chunks for every new piece I added. It got me thinking, "What if I could skip all that and just dive into the fun part – the actual logic and features?"
That lightbulb switched on when I was messing around with gRPC. It was awesome how it spit out basic code straight from those .proto definitions. Given that tools like NestJS, React, and Next.js in the Node world are all about generating code efficiently, I figured it was time to make a personalized generator that fits our team's setup perfectly.
Step One: Getting Things Organized for Smooth Automation
To make this generator work like a charm, I had to lay down some solid ground rules first. Automating a mess just creates more mess, right? So, I locked in the classic Controller-Service-Repository setup and set firm guidelines for how data moves around with DTOs.
Setting Up Uniform DTOs for Inputs and Outputs
I designed a reliable format that every API output follows without fail.
{
"statusCode": 200,
"message": "Success",
"data": {
"id": 1,
"name": "John Doe"
}
}
Here's the breakdown:
- The
datapart is always an object, and if it's a collection, you'll find anitemsarray inside. - Don't forget the required
statusCodeandmessageelements.
On the input side, I put together foundational classes to cover everyday needs, such as handling paginated queries or admin-level access.
// Example: Composing DTOs using IntersectionType
export class DomainGetDto extends IntersectionType(BasePaginationRequestDto, AdminRequestDto) {}
class DomainResponseData {
@ApiProperty({ item: DomainResponseDataItem, isArray: true })
items: DomainResponseDataItem[];
}
export class DomainResponse extends BaseResponse {
@ApiProperty({ type: DomainResponseData })
data: DomainResponseData;
}
Clarifying What Each Layer Does
To ensure the generator could predict and produce code reliably, I drew clear lines between what goes where.
- Controller:
- It grabs incoming DTOs from queries or bodies and sends them straight over to the Service layer.
- Then, it takes whatever data the Service provides, wraps it up in a
BaseResponse, and sends it back. - It also manages things like authentication checks and keeping logs.
- Service:
- This is all about the core operations and logic.
- It just delivers the essential data without any extras.