
A declarative toolkit for routing and validation in Express, built with TypeScript and Decorators.
GatEX simplifies the creation of robust APIs by allowing the organization of routes into groups and the definition of complete RESTful endpoints using classes and decorators, with native integration with Zod for validation.
body, query, and params simply and directly.GatEX requires peer dependencies. Install everything needed before using Gatex:
npm install express zod reflect-metadata gatex
Or using Yarn:
yarn add express zod reflect-metadata gatex
For decorators to work correctly, two configurations are essential.
tsconfig.jsonYour tsconfig.json file needs to have the following compilerOptions enabled:
{
"compilerOptions": {
// ...
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
reflect-metadataYou must import reflect-metadata once, and only once, as the very first line of your application's entry file.
// In your main file, e.g., server.ts or index.ts
import "reflect-metadata";
// The rest of your code comes after...
import express from "express";
This is a complete example of a basic server using Gatex.
// server.ts
import "reflect-metadata";
import express from "express";
import { GroupingProvider } from "gatex";
const app = express();
app.use(express.json()); // Middleware for JSON parsing
const provider = new GroupingProvider();
// Basic route
provider.get("/ping", (req, res) => {
res.status(200).json({ message: "pong" });
});
// Applies the routes and error handlers to the Express app
provider.finish(app);
app.listen(3000, () => {
console.log("🚀 Server running on http://localhost:3000");
});
You can apply as many middlewares as you want to individual routes or entire groups.
const authMiddleware = (req, res, next) => {
console.log("Auth Middleware");
// Authentication logic here...
next();
};
// Middleware applied to a specific route
provider.get("/profile", authMiddleware, (req, res) => {
res.send("User Profile");
});
// Middleware applied to a group of routes
provider.group("/api/v1", authMiddleware, (v1) => {
// All routes within this group will go through the authMiddleware
v1.get("/users", (req, res) => {
res.send("Users list");
});
v1.get("/products", (req, res) => {
res.send("Products list");
});
});
This is the main feature of Gatex. Define a complete RESTful API in a class/repository.
// UserRepository.ts
import { Request, Response } from "express";
import { z } from "zod";
import { IRepository, Schema, Middleware, PathName, IdParam } from "gatex";
const authMiddleware = (req, res, next) => {
console.log("Auth Middleware running for a repository method!");
next();
};
const userCreateSchema = z.object({
name: z.string().min(3),
email: z.string().email(),
});
@PathName("users") // Sets the base path to /users
@IdParam("userId") // Sets the ID parameter name to :userId
export class UserRepository implements IRepository {
// Mapped to: POST /users
@Schema({ body: userCreateSchema })
@Middleware(authMiddleware)
create(req: Request, res: Response) {
const newUser = req.body;
res.status(201).json({ message: "User created", user: newUser });
}
// Mapped to: GET /users/:userId
get(req: Request, res: Response) {
const { userId } = req.params;
res.json({ id: userId, name: "John Doe" });
}
}
import { UserRepository } from "./UserRepository";
const provider = new GroupingProvider();
provider.repository(new UserRepository());
provider.finish(app);
The code above will automatically generate and configure the following routes:
| Verb | Route | Repository Method | Middlewares |
|---|---|---|---|
POST |
/users |
create |
authMiddleware, Schema Validation |
GET |
/users/:userId |
get |
- |
Your repository can implement any of the following methods.
| Method in Class | HTTP Verb | Generated Route |
|---|---|---|
create |
POST |
/{pathName} |
list |
GET |
/{pathName} |
get |
GET |
/{pathName}/:{idParam} |
update |
PUT |
/{pathName}/:{idParam} |
patch |
PATCH |
/{pathName}/:{idParam} |
delete |
DELETE |
/{pathName}/:{idParam} |
- You don't need to implement all of them.
Gatex uses Zod for validation. If a schema fails, a detailed 400 Bad Request error response is sent automatically.
Example of a default error response:
{
"error": true,
"content": {
"message": "Validation Error",
"request": { "body": { "name": "Jo" } },
"schemaIssues": [
{
"attribute": "body.name",
"error": "String must contain at least 3 character(s)"
}
]
}
}
You can easily override the default handler for validation errors and format the response as you wish.
const provider = new GroupingProvider();
provider.schemeErrorHandler = (err, req, res, next) => {
// 'err' is an instance of ValidationSchemeError
res.status(400).json({
message: "The submitted data is invalid.",
errors: err.issues.map((issue) => ({
field: issue.path.join("."),
problem: issue.message,
})),
});
};
// ... register your routes and repositories
provider.finish(app);
To run the integration test suite, use:
npm test
Contributions are welcome! Feel free to open an issue or submit a pull request.