Compare commits

...

3 Commits

  1. 198
      README.md
  2. 2
      src/app.module.ts
  3. 1
      src/core/constants/index.ts
  4. 3
      src/core/database/database.providers.ts
  5. 6
      src/modules/orders/orders.controller.ts
  6. 11
      src/modules/orders/orders.service.ts
  7. 13
      src/modules/users/entities/user.entity.ts
  8. 3
      src/modules/wallets/dto/update-wallet.dto.ts
  9. 26
      src/modules/wallets/entities/wallet.entity.ts
  10. 24
      src/modules/wallets/wallets.controller.ts
  11. 11
      src/modules/wallets/wallets.module.ts
  12. 9
      src/modules/wallets/wallets.providers.ts
  13. 45
      src/modules/wallets/wallets.service.ts

@ -1,99 +1,151 @@
<p align="center"> # CRUD
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Project setup
```bash # Backend API for E-commerce Application
$ npm install
```
## Compile and run the project This is a simple **Backend Service API** for a **E-commerce Application** with various endpoints built using **Nest.js**, **PostgreSQL** & **Sequelize**.
```bash ---
# development
$ npm run start
# watch mode ## Features
$ npm run start:dev
# production mode - Built with **Nest.js**.
$ npm run start:prod - **PostgreSQL** and **Sequelize** for database management.
``` - Implements **JWT Authentication**.
- RESTful APIs for user management:
- Create, Read, Update, and Delete (CRUD) operations.
## Run tests ---
```bash ## Technologies Used
# unit tests
$ npm run test
# e2e tests - **Nest.js**
$ npm run test:e2e - **PostgreSQL** with **Sequelize**
- **JWT** for authentication
- **ARGON2** for password hashing
# test coverage ---
$ npm run test:cov
``` ## Installation and Setup
## Deployment ### Prerequisites
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. - Node.js and npm installed on your system.
- postgreSQL connection (local or cloud).
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: ### 1. Clone the Repository
```bash ```bash
$ npm install -g mau git clone https://github.com/MahdiTajikKazemi/e-commerce-api-nestjs-postgres-sequelize
$ mau deploy
``` ```
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. ### 2. Setup Backend
1. Install dependencies:
```bash
npm install
```
2. Create a `.env` file in the `backend` directory and configure it:
```env
Set your environment variables in .env.sample file.
```
3. Start the server:
```bash
npm run start:dev
```
## Usage
## Resources 1. Start the backend server.
2. Open your 'Postman' | 'insomnia' or whatever testing api app you are comfortable using and then begin hitting enpoints listed below.
Check out a few resources that may come in handy when working with NestJS: ---
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. ## API Endpoints
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
## Support ### Authentication
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). - `POST /api/v1/auth/signup`: Register a new user and get a JWT token.
- `POST /api/v1/auth/login`: Login and get a JWT token.
## Stay in touch ### User Management
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) - `GET /api/v1/users/`: Get a list of all users (admin only).
- Website - [https://nestjs.com](https://nestjs.com/) - `POST /api/v1/users/`: Create a user (admin only).
- Twitter - [@nestframework](https://twitter.com/nestframework) - `PATCH /api/v1/users/:id`: Update a specific user (admin only).
- `DELETE /api/v1/users/:id`: Remove a specific user (admin only).
- `GET /api/v1/users/:id`: Get a specific user (admin only).
---
## Folder Structure
```
└── 📁src
└── 📁core
└── 📁constants
└── index.ts
└── 📁database
└── database.config.ts
└── database.module.ts
└── database.providers.ts
└── 📁interfaces
└── dbConfig.interface.ts
└── 📁modules
└── 📁auth
└── auth.controller.ts
└── auth.module.ts
└── auth.service.ts
└── jwt.strategy.ts
└── local.strategy.ts
└── 📁orders
└── 📁dto
└── create-order.dto.ts
└── index.ts
└── update-order.dto.ts
└── 📁entities
└── order-status.entity.ts
└── order.entity.ts
└── order-statuses.providers.ts
└── orders.controller.ts
└── orders.module.ts
└── orders.providers.ts
└── orders.service.ts
└── 📁products
└── 📁dto
└── create-product.dto.ts
└── index.ts
└── update-product.dto.ts
└── 📁entities
└── product.entity.ts
└── products.controller.ts
└── products.module.ts
└── products.providers.ts
└── products.service.ts
└── 📁users
└── 📁dto
└── create-user.dto.ts
└── index.ts
└── update-user.dto.ts
└── 📁entities
└── user.entity.ts
└── users.controller.ts
└── users.module.ts
└── users.providers.ts
└── users.service.ts
└── app.controller.ts
└── app.module.ts
└── app.service.ts
└── main.ts
```
## Future Enhancements
- Integrate redis for cashing
---
## License ## License
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). This project is licensed under the MIT License.
Mahdi Tajik Kazemi

@ -8,6 +8,7 @@ import { UsersController } from './modules/users/users.controller';
import { ProductsModule } from './modules/products/products.module'; import { ProductsModule } from './modules/products/products.module';
import { AuthModule } from './modules/auth/auth.module'; import { AuthModule } from './modules/auth/auth.module';
import { OrdersModule } from './modules/orders/orders.module'; import { OrdersModule } from './modules/orders/orders.module';
import { WalletsModule } from './modules/wallets/wallets.module';
@Module({ @Module({
imports: [ imports: [
@ -17,6 +18,7 @@ import { OrdersModule } from './modules/orders/orders.module';
ProductsModule, ProductsModule,
AuthModule, AuthModule,
OrdersModule, OrdersModule,
WalletsModule,
], ],
controllers: [AppController, UsersController], controllers: [AppController, UsersController],
providers: [AppService], providers: [AppService],

@ -6,3 +6,4 @@ export const USER_REPOSITORY = 'USER_REPOSITORY';
export const PRODUCT_REPOSITORY = 'PRODUCT_REPOSITORY'; export const PRODUCT_REPOSITORY = 'PRODUCT_REPOSITORY';
export const ORDER_REPOSITORY = 'ORDER_REPOSITORY'; export const ORDER_REPOSITORY = 'ORDER_REPOSITORY';
export const ORDER_STATUSES_REPOSITORY = 'ORDER_STATUSES_REPOSITORY'; export const ORDER_STATUSES_REPOSITORY = 'ORDER_STATUSES_REPOSITORY';
export const WALLET_REPOSITORY = 'WALLET_REPOSITORY';

@ -5,6 +5,7 @@ import { User } from 'src/modules/users/entities/user.entity';
import { Product } from 'src/modules/products/entities/product.entity'; import { Product } from 'src/modules/products/entities/product.entity';
import { OrderStatuses } from 'src/modules/orders/entities/order-status.entity'; import { OrderStatuses } from 'src/modules/orders/entities/order-status.entity';
import { Order } from 'src/modules/orders/entities/order.entity'; import { Order } from 'src/modules/orders/entities/order.entity';
import { Wallet } from 'src/modules/wallets/entities/wallet.entity';
export const databaseProviders = [ export const databaseProviders = [
{ {
@ -25,7 +26,7 @@ export const databaseProviders = [
config = databaseConfig.development; config = databaseConfig.development;
} }
const sequelize = new Sequelize(config); const sequelize = new Sequelize(config);
sequelize.addModels([User, Product, Order, OrderStatuses]); sequelize.addModels([User, Product, Order, OrderStatuses, Wallet]);
await sequelize.sync(); await sequelize.sync();
return sequelize; return sequelize;
}, },

@ -10,6 +10,7 @@ import {
import { OrdersService } from './orders.service'; import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto'; import { CreateOrderDto } from './dto/create-order.dto';
import { UpdateOrderDto } from './dto/update-order.dto'; import { UpdateOrderDto } from './dto/update-order.dto';
import { UUID } from 'crypto';
@Controller('orders') @Controller('orders')
export class OrdersController { export class OrdersController {
@ -25,6 +26,11 @@ export class OrdersController {
return this.ordersService.findAll(); return this.ordersService.findAll();
} }
@Get('users/:id')
findByUser(@Param('id') id: UUID) {
return this.ordersService.findByUser(id);
}
@Get(':id') @Get(':id')
findOne(@Param('id') id: string) { findOne(@Param('id') id: string) {
return this.ordersService.findOne(+id); return this.ordersService.findOne(+id);

@ -4,6 +4,7 @@ import { Order } from './entities/order.entity';
import { ORDER_REPOSITORY } from 'src/core/constants'; import { ORDER_REPOSITORY } from 'src/core/constants';
import { OrderStatuses } from './entities/order-status.entity'; import { OrderStatuses } from './entities/order-status.entity';
import { User } from '../users/entities/user.entity'; import { User } from '../users/entities/user.entity';
import { UUID } from 'crypto';
@Injectable() @Injectable()
export class OrdersService { export class OrdersService {
@ -30,6 +31,16 @@ export class OrdersService {
}); });
} }
async findByUser(id: UUID) {
return await this.orderRepository.findAll({
include: {
model: User,
where: { uuid: id },
attributes: ['firstName', 'lastName'],
},
});
}
async update(id: number, updateOrderDto: UpdateOrderDto) { async update(id: number, updateOrderDto: UpdateOrderDto) {
const [numberOfAffectedRows, [updatedOrder]] = const [numberOfAffectedRows, [updatedOrder]] =
await this.orderRepository.update( await this.orderRepository.update(

@ -1,5 +1,13 @@
import { Table, Column, Model, DataType, HasMany } from 'sequelize-typescript'; import {
Table,
Column,
Model,
DataType,
HasMany,
HasOne,
} from 'sequelize-typescript';
import { Order } from '../../orders/entities/order.entity'; import { Order } from '../../orders/entities/order.entity';
import { Wallet } from 'src/modules/wallets/entities/wallet.entity';
@Table({ tableName: 'users' }) @Table({ tableName: 'users' })
export class User extends Model<User> { export class User extends Model<User> {
@ -56,4 +64,7 @@ export class User extends Model<User> {
@HasMany(() => Order) @HasMany(() => Order)
orders: Order[]; orders: Order[];
@HasOne(() => Wallet)
wallet: Wallet;
} }

@ -0,0 +1,3 @@
export class UpdateWalletDto {
amount: number;
}

@ -0,0 +1,26 @@
import {
Table,
Column,
Model,
DataType,
ForeignKey,
BelongsTo,
} from 'sequelize-typescript';
import { User } from 'src/modules/users/entities/user.entity';
@Table({ tableName: 'wallets', createdAt: false })
export class Wallet extends Model<Wallet> {
@Column({
type: DataType.FLOAT,
allowNull: false,
defaultValue: 0,
})
balance: number;
@ForeignKey(() => User)
@Column
userId: number;
@BelongsTo(() => User)
user: User;
}

@ -0,0 +1,24 @@
import { Body, Controller, Get, Param, Patch } from '@nestjs/common';
import { WalletsService } from './wallets.service';
import { UUID } from 'crypto';
import { UpdateWalletDto } from './dto/update-wallet.dto';
@Controller('wallets')
export class WalletsController {
constructor(private readonly walletsService: WalletsService) {}
@Get('balance/:id')
getWallet(@Param('id') id: UUID) {
return this.walletsService.getWallet(id);
}
@Patch('deposit/:id')
deposit(@Param('id') id: UUID, @Body() updateWalletDto: UpdateWalletDto) {
return this.walletsService.deposit(id, updateWalletDto);
}
@Patch('withdraw/:id')
withdraw(@Param('id') id: UUID, @Body() updateWalletDto: UpdateWalletDto) {
return this.walletsService.withdraw(id, updateWalletDto);
}
}

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { WalletsService } from './wallets.service';
import { WalletsController } from './wallets.controller';
import { walletsProviders } from './wallets.providers';
@Module({
controllers: [WalletsController],
providers: [WalletsService, ...walletsProviders],
exports: [WalletsService],
})
export class WalletsModule {}

@ -0,0 +1,9 @@
import { Wallet } from './entities/wallet.entity';
import { WALLET_REPOSITORY } from '../../core/constants';
export const walletsProviders = [
{
provide: WALLET_REPOSITORY,
useValue: Wallet,
},
];

@ -0,0 +1,45 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { UUID } from 'crypto';
import { WALLET_REPOSITORY } from 'src/core/constants';
import { Wallet } from './entities/wallet.entity';
import { User } from '../users/entities/user.entity';
import { UpdateWalletDto } from './dto/update-wallet.dto';
@Injectable()
export class WalletsService {
constructor(
@Inject(WALLET_REPOSITORY) private readonly walletRepository: typeof Wallet,
) {}
async getWallet(id: UUID) {
return await this.walletRepository.findAll({
include: {
model: User,
where: { uuid: id },
attributes: ['firstName'],
},
});
}
async deposit(id: UUID, updateWalletDto: UpdateWalletDto) {
const { amount } = updateWalletDto;
if (amount < 0) throw new BadRequestException();
const [wallet] = await this.getWallet(id);
wallet.balance += amount;
return wallet.save();
}
async withdraw(id: UUID, updateWalletDto: UpdateWalletDto) {
const { amount } = updateWalletDto;
if (amount < 0) throw new BadRequestException();
const [wallet] = await this.getWallet(id);
wallet.balance -= amount;
return wallet.save();
}
}
Loading…
Cancel
Save