User Management API
This example walks you through building a LightTs API to manage users, complete with a controller, service, validator, JWT authentication, CORS, and PostgreSQL integration. It’s a practical way to see how LightTs’s modular structure and CLI streamline development.
-
Initialize Project
Terminal window lts init user-api -
Add Features
Terminal window lts add databaselts add validationlts add jwtlts add corsSelect PostgreSQL for the database and enable migrations.
-
Generate User Module
Terminal window lts g resource userThis creates
src/modules/user/
withuser.controller.ts
,user.service.ts
, anduser.schema.ts
. -
Configure Database
Set up the database connection and entity.
-
Define Routes
Create secure, validated routes for user operations.
Project Structure
Section titled “Project Structure”Directoryuser-api/
- …
Directorysrc/
Directorycore/
- cors.core.ts
- logger.core.ts
Directorydatabase/
Directoryentities/
- user.entity.ts
- index.ts
Directorymodules/
Directoryuser/
- user.controller.ts
- user.service.ts
- user.schema.ts
Directorymiddleware/
- auth.middleware.ts
- config.ts
- routes.ts
- index.ts
Code Examples
Section titled “Code Examples”export const { db, auth } = { db: { user: process.env.DB_USER, name: process.env.DB_NAME, pwd: process.env.DB_PWD, host: process.env.DB_HOST, port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 5432 }, auth: { salt: 10, jwt: { access: { secret: process.env.ACCESS_JWT_SECRET, expiresIn: '15m' }, refresh: { secret: process.env.REFRESH_JWT_SECRET, expiresIn: '30d' } } },};
import { db } from '@/config';import path from 'path';import { DataSource } from 'typeorm';import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
export const AppDataSource = new DataSource({ type: 'postgres', host: db.host, port: db.port, username: db.user, password: db.pwd, database: db.name, synchronize: false, logging: false, entities: [path.join(__dirname, '/entities/**/*.entity.{ts,js}')], migrations: [path.join(__dirname, '/migrations/**/*.{ts,js}')], subscribers: [], migrationsTableName: 'migrations', namingStrategy: new SnakeNamingStrategy(),});
export async function createConnection() { if (!AppDataSource.isInitialized) { try { await AppDataSource.initialize(); } catch (error) { throw error; } }}
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()export class User { @PrimaryGeneratedColumn() id: number;
@Column() name: string;
@Column() email: string;
@Column() password: string;}
import { BadRequestError } from '@/core/errors/bad-request.error';import { DataResponse } from '@/core/responses/data.response';import { MessageResponse } from '@/core/responses/message.response';import { Request, Response } from 'express';import { AppDataSource } from '@/database';import { User } from '@/database/entities/user.entity';
export default { get: async (req: Request, res: Response) => { const userRepo = AppDataSource.getRepository(User); const users = await userRepo.find();
return new DataResponse(res, { data: users, message: 'Users fetched like a pro!', statusCode: 200, }); }, create: async (req: Request, res: Response) => { const { name, email, password } = req.body;
const userRepo = AppDataSource.getRepository(User); const existing = await userRepo.findOneBy({ email });
if (existing) throw new BadRequestError('Email already exists!', { field: 'email' });
const user = userRepo.create({ name, email, password }); await userRepo.save(user);
return new MessageResponse(res, { message: 'User created, high five!', statusCode: 201 }); }};
import Joi from 'joi';
export default { create: Joi.object().keys({ name: Joi.string().required(), email: Joi.string().email().required(), password: Joi.string().min(6).required(), }),};
import express from 'express';import services from './user.service';import schema from './user.schema';import validate from '@/middleware/validator.middleware';import { validateToken } from '@/middleware/auth.middleware';
const router = express.Router();
router.get('/', services.get);router.post('/', validate(schema.create), validateToken(['admin']), services.create);
export default router;
import express from 'express';import helloController from '@/modules/hello/hello.controller';import userController from "@/modules/user/user.controller";
const router = express.Router();
router.use('/user', userController);
export default router;
import express from 'express';import { HttpError } from '@/core/errors';import { api } from '@/config';import log from '@/core/logger.core';import routes from '@/routes';import cors from 'cors';import { checkCorsOrigin } from '@/core/cors.core';import { createConnection } from '@/database';import 'tsconfig-paths/register';
const app = express();
// express configurationsapp.use(express.json());app.use(express.urlencoded({ extended: true }));
// corsapp.use( cors({ origin: checkCorsOrigin, credentials: true }));
// database connectioncreateConnection() .then(() => { log.info('Connected to database'); }) .catch((error) => { log.error('Error connecting to database:'); log.error(error); });
// http routesapp.use(`/api/${api.version}`, routes);
// http error class handlerapp.use(HttpError.middleware);
app.listen(api.port, () => { log.info(`Listening on port: ${api.port}`);});
Run the API
Section titled “Run the API”-
Set Environment Variables
Create a
.env
file:Terminal window # databaseDB_HOST=localhostDB_PORT=5432DB_USER=postgresDB_PASSWORD=passwordDB_NAME=my_api_db# jwtACCESS_JWT_SECRET=your-secret -
Run Migrations
Terminal window npm run migrate -
Start the Server
Terminal window npm run dev -
Test the API
Use Postman or curl to test:
POST /api/users
with a valid JWT and body:{ "name": "John Doe", "email": "[email protected]", "password": "secure123" }
GET /api/users
with a valid JWT
- Customization: Edit files in
src/modules/user/
andsrc/database/
to suit your needs. - Security: Use a secure
ACCESS_JWT_SECRET
and setsynchronize: false
in production. - Scalability: Add more entities or services to expand the API.
Add More Features Integrate additional features like validation or CORS.