In this tutorial, we’ll walk through building a robust e-commerce backend API using TyphoonTS, TypeORM, and MongoDB with TypeScript. We’ll cover initializing the project, setting up TypeORM with MongoDB, defining entities, creating services and controllers, and running the server with Nodemon for a seamless development experience. By the end, you’ll have a scalable backend ready for your e-commerce application, leveraging the power of TypeScript’s type safety and the flexibility of MongoDB. Let’s get started on this exciting journey!
TyphoonTS is chosen for its robust and type-safe framework, making it ideal for building scalable web applications in TypeScript. So, let’s get started…
1. Initialize Your Project
mkdir -p ecommerce-backend/src
cd ecommerce-backend
npm init -y
npm install typhoonts typeorm reflect-metadata mongodb joi
npm install -D @types/node nodemon ts-node ts-node-dev typescript
2. Set Up TypeScript Configuration
Create a tsconfig.json
file in the root directory:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
3. Configure TypeORM
In this step, we configure TypeORM to connect to our MongoDB database and manage our entities.
Create a data-source.ts
file in the src
directory:
import { DataSource } from "typeorm";
import Product from "./entities/Product";
export const AppDataSource = new DataSource({
type: "mongodb",
url: "mongodb://127.0.0.1:27017/ecommerce",
synchronize: true,
useUnifiedTopology: true,
entities: [Product],
});
Explanation:
- DataSource: This is TypeORM’s primary configuration object for database connections.
- type: Specifies the database type; here, it’s MongoDB.
- url: The connection URL to the MongoDB server.
- synchronize: Automatically synchronizes the database schema with the entity definitions.
- useUnifiedTopology: Enables the new unified topology layer for MongoDB.
- entities: Lists the entities to be managed by TypeORM. Here, it’s the Product entity.
This configuration initializes the connection to the MongoDB database and sets up TypeORM to manage the specified entities.
4. Define Your Product Entity
Create an entities
directory inside src
and add Product.ts
:
import { Column, Entity, ObjectId, ObjectIdColumn } from "typeorm";
@Entity({ name: "products" })
class Product {
@ObjectIdColumn()
id!: ObjectId;
@Column()
name!: string;
@Column()
price!: number;
@Column()
description!: string;
}
export default Product;
Explanation:
- @Entity: Marks the class as a database entity, with the table name “products”.
- @ObjectIdColumn: Specifies the id field as the primary key and stores MongoDB ObjectIds.
- @Column: Indicates that the following fields are columns in the database table.
- id: Unique identifier for each product.
- name: Name of the product.
- price: Price of the product.
- description: Description of the product.
This defines the Product entity structure in TypeORM, mapping it to a MongoDB collection.
5. Create Product DAO(Data Access Object)
Create a dao
directory and add ProductDao.ts
:
interface ProductDao {
name: string;
price: number;
description: string;
}
export default ProductDao;
Explanation:
- ProductDao Interface: Defines the data structure for Product data access objects.
- name: Represents the name of the product.
- price: Represents the price of the product.
- description: Represents the description of the product.
This interface outlines the shape of the product data, ensuring consistency in how product information is accessed and manipulated throughout the application.
6. Create Product DTO(Data Access Object)
Create a dto
directory and add ProductDto.ts
:
export interface ProductListDto {
id: string;
name: string;
price: number;
}
Explanation:
- ProductListDto Interface: Defines the data transfer object structure for listing products.
- id: Represents the unique identifier of the product.
- name: Represents the name of the product.
- price: Represents the price of the product.
This DTO specifies the shape of the product data to be transferred between processes or over the network, ensuring consistency and type safety.
7. Create Product Service
Create a services
directory and add ProductService.ts
:
import { ObjectId } from "mongodb";
import { Service } from "typhoonts";
import ProductDao from "../dao/ProductDao";
import { AppDataSource } from "../data-source";
import Product from "../entities/Product";
@Service()
class ProductService {
private productRepository = AppDataSource.getMongoRepository(Product);
async create(product: ProductDao) {
return this.productRepository.save(product);
}
async getAll() {
return (await this.productRepository.find()).map((product: Product) => ({
id: product.id.toString(),
name: product.name,
price: product.price,
}));
}
async get(id: string) {
const productId = new ObjectId(id);
return this.productRepository.findOne({ where: { _id: productId } });
}
async update(id: string, product: ProductDao) {
return this.productRepository.update(id, product);
}
}
export default ProductService;
Explanation:
- ProductService Class: Provides methods to manage product data.
- productRepository: Accesses the MongoDB collection for products.
- create: Saves a new product to the database.
- getAll: Retrieves all products, mapping them to a simplified structure.
- get: Retrieves a single product by its ID.
- update: Updates an existing product with new data. This service encapsulates the business logic for handling product data, interacting with the database through TypeORM.
8. Create Product Controller
Create a controllers
directory and add ProductController.ts
:
import Joi from "joi";
import {
Body,
Controller,
Get,
Inject,
Param,
Post,
Put,
ResponseBody,
} from "typhoonts";
import ProductDao from "../dao/ProductDao";
import { ProductListDto } from "../dto/ProductDto";
import Product from "../entities/Product";
import ProductService from "../services/ProductService";
@Controller("/products")
class ProductController {
@Inject(ProductService)
private productService!: ProductService;
@Post("/")
@ResponseBody()
async create(@Body() productReqDao: ProductDao) {
const productSchema = Joi.object({
name: Joi.string().min(3).max(30).required(),
price: Joi.number().positive().required(),
description: Joi.string().optional(),
});
const productValidate = await productSchema.validate(productReqDao);
if (productValidate?.error) {
return { status: 400, error: productValidate.error.details };
}
const product: Product = await this.productService.create(productReqDao);
return {
message: `${product.name} has been created`,
};
}
@Get("/")
@ResponseBody()
async getAll() {
const products: ProductListDto[] = await this.productService.getAll();
return products;
}
@Get("/:id")
@ResponseBody()
async get(@Param("id") id: string) {
const product: Product | null = await this.productService.get(id);
return (
product || {
message: "Product not found",
}
);
}
@Put("/:id")
@ResponseBody()
async update(@Param("id") id: string, @Body() productReqDao: ProductDao) {
const productSchema = Joi.object({
name: Joi.string().min(3).max(30).required(),
price: Joi.number().positive().required(),
description: Joi.string().optional(),
});
const productValidate = await productSchema.validate(productReqDao);
if (productValidate?.error) {
return { status: 400, error: productValidate.error.details };
}
await this.productService.update(id, productReqDao);
return {
message: `${productReqDao.name} has been updated`,
};
}
}
export default ProductController;
Explanation:
- ProductController Class: Manages HTTP requests for product operations.
- @Inject(ProductService): Injects the ProductService into the controller.
- create: Validates and creates a new product using ProductService.
- getAll: Retrieves all products via ProductService.
- get: Retrieves a specific product by ID via ProductService.
- update: Validates and updates an existing product using ProductService.
- Validation: Uses Joi for request data validation.
- ResponseBody: Ensures the responses are in JSON format.
This controller handles the endpoints for creating, retrieving, and updating products, utilizing the ProductService for business logic and Joi for request validation.
9. Set Up the Server
Create a server.ts
file in the src
directory:
import "reflect-metadata";
import { Server } from "typhoonts";
import ProductController from "./controllers/ProductController";
import { AppDataSource } from "./data-source";
const server = new Server({
useBodyParser: true,
useCookieParser: true,
useSession: true,
sessionOptions: { secret: "my_secret_key" },
});
server.registerController(ProductController);
server.listen(8000, () => {
console.log("server is running on http://localhost:8000");
AppDataSource.initialize()
.then(async () => {
console.log("DB initialize");
})
.catch((error) =>
console.log("Error during Data Source initialization:", error)
);
});
Explanation:
- Imports and Initialization: Imports necessary modules and initializes the server.
- Server Configuration: Configures the server to use body parser, cookie parser, and session with a secret key.
- Register Controller: Registers the ProductController to handle product-related routes.
- Start Server: Starts the server on port 8000 and logs the URL.
- Initialize Database: Initializes the database connection using AppDataSource and logs success or error messages.
This sets up and runs the server, connecting it to the database and registering the product routes.
10. Set Up Nodemon
Add a nodemon.json
file in the root of your project:
{
"watch": [
"src"
],
"ext": "ts",
"ignore": [
"src/**/*.spec.ts"
],
"exec": "ts-node-dev src/server.ts"
}
11. Update package.json Scripts
Add a dev script to your package.json
:
"scripts": {
"start": "ts-node src/server.ts",
"build": "tsc",
"dev": "nodemon"
},
12. Run the Project
npm run dev
Conclusion
You’ve now set up a basic e-commerce backend API using TyphoonTS, TypeORM, and MongoDB with TypeScript. This setup includes entity definitions, service layers, and controllers, all while leveraging TypeORM for database operations and TyphoonTS for server and routing management. Happy coding!
Feel free to reach out to me if you have any questions or need further assistance. Connect with me on LinkedIn.
Top comments (0)