Code Monkey home page Code Monkey logo

baguilar6174 / node-template-server Goto Github PK

View Code? Open in Web Editor NEW
58.0 4.0 17.0 280 KB

Boilerplate Node projects with Express, Typescript and Clean Architecture

Home Page: https://baguilar6174.medium.com/modern-api-development-with-node-js-express-and-typescript-using-clean-architecture-0868607b76de

TypeScript 100.00%
api-rest biolerplate eslint express jest nodejs prettier template-project typescript api clean-architecture dto-pattern repository-pattern usecase adapter-pattern supertest unit-testing

node-template-server's Introduction

Node Template REST API

This reporsitory contains a template for projects with Node, Express, Typescript. The test environment has been configured using Jest, and ESLint and Prettier have been integrated to set code style definitions. You can find the step-by-step construction of this project in this article:

Boilerplate for your Node projects with Express

Explore the world of API development using Node.js, Express, and TypeScript. Learn how to implement Clean Architecture and best programming practices to create robust, scalable, and maintainable web services. Whether you're a seasoned developer or just starting out, this repository provides comprehensive resources, tutorials, and examples to help you master API development with confidence.

Modern API Development with Node.js, Express, and TypeScript using Clean Architecture

Installation

Clone this repository

git clone https://github.com/baguilar6174/node-template-server.git

Install dependencies

yarn

Clone .env.template file and rename to .env.

Replace your environment variables in .env file

Running the app

Run yarn dev

If your want to create build production, run yarn build

If your want to run tests, run yarn test || yarn test:watch

My process

Built with

  • Node
  • Typescript
  • Express
  • ESLint & Prettier
  • Environment Variables
  • Unit testing with Jest & Supertest
  • Clean Architecture
  • Repository Pattern
  • Adapter Pattern
  • Use Cases
  • DTOs (Data Transfer Objects)

API Documentation

This entire implementation is based on an in-memory database for example purposes. NO real database is being used, when the server is re-run, the in-memory database will be re-created.

Also, simple implementations have been created to encrypt and validate authentication tokens (no third party dependencies are used for this). If you use this repository as an example, it is recommended that you modify these implementations using libraries or your own implementations (src/core/config folder).

Authentication

POST /api/v1/auth/register

Registers a new user in memory database.

Request Body

{
	"name": "string",
	"email": "string",
	"password": "string"
}

Response Codes

  • 400 Bad Request: Returned if the request body is invalid.
  • 201 Created: Returned if the request is successful.

Response

{
	"data": {
		"user": {
			"id": "string",
			"name": "string",
			"email": "string",
			"emailVerified": "boolean",
			"role": "string[]"
		},
		"token": "string"
	}
}

POST /api/v1/auth/login

Logs in a user.

Request Body

{
	"email": "string",
	"password": "string"
}

Response Codes

  • 400 Bad Request: Returned if the request body is invalid.
  • 200 OK: Returned if the request is successful.

Response

{
	"data": {
		"user": {
			"id": "string",
			"name": "string",
			"email": "string",
			"emailVerified": "boolean",
			"role": "string[]"
		},
		"token": "string"
	}
}

GET /api/v1/todos

Retrieves a paginated list of todos.

Query Parameters

  • page (number, optional): The page number to retrieve. Defaults to 1.
  • limit (number, optional): The number of items per page. Defaults to 10.

Response Codes

Ensure that the values for page and limit are valid positive integers to avoid errors.

  • 400 Bad Request: Returned if query parameters are invalid.
  • 200 OK: Returned if the request is successful.

Response

{
	"data": {
		"results": [
			{
				"id": "number",
				"text": "string",
				"isCompleted": "boolean"
			}
		],
		"currentPage": "number",
		"nextPage": "number | null",
		"prevPage": "number | null",
		"total": "number",
		"totalPages": "number"
	}
}

GET /api/v1/todos/:id

Retrieves a single todo item by its id.

Path Parameters

  • id (number): The id of the todo item to retrieve.

Response Codes

  • 404 Not Found: Returned if the todo item with the specified id does not exist.
  • 200 OK: Returned if the request is successful.

Response

{
	"data": {
		"id": "number",
		"text": "string",
		"isCompleted": "boolean"
	}
}

POST /api/v1/todos

Creates a new todo item. You need to be logged in previously. This endpoint requires authorization because it is protected with AuthMiddleware.

Authorization

Bearer <token>

Request Body

{
	"text": "string"
}

Response Codes

  • 401 Unauthorized: Returned if the request is not authorized.
  • 400 Bad Request: Returned if the request body is invalid.
  • 201 Created: Returned if the request is successful.

Response

{
	"data": {
		"id": "number",
		"text": "string",
		"isCompleted": "boolean"
	}
}

PUT /api/v1/todos/:id

Updates a todo item properties by its id.

Path Parameters

  • id (number): The id of the todo item to update.

Request Body

{
	"text": "string",
	"isCompleted": "boolean"
}

Response Codes

  • 404 Not Found: Returned if the todo item with the specified id does not exist.
  • 400 Bad Request: Returned if the request body is invalid.
  • 200 OK: Returned if the request is successful.

Response

{
	"data": {
		"id": "number",
		"text": "string",
		"isCompleted": "boolean"
	}
}

DELETE /api/v1/todos/:id

Deletes a todo item by its id.

Path Parameters

  • id (number): The id of the todo item to delete.

Response Codes

  • 404 Not Found: Returned if the todo item with the specified id does not exist.
  • 200 OK: Returned if the request is successful.

Response

{
	"data": {
		"id": "number",
		"text": "string",
		"isCompleted": "boolean"
	}
}

Project Structure

node-template-server/
│
├── dist/
├── node_modules/
├── src/
│   ├── core/
│   │   ├── config/
│   │   ├── constants/
│   │   ├── errors/
│   │   └── types/
│   ├── features/
│   │   ├── auth/
│   │   │   ├── domain/
│   │   │   │   ├── datasources/
│   │   │   │   ├── dtos/
│   │   │   │   ├── entities/
│   │   │   │   ├── repositories/
│   │   │   │   └── usecases/
│   │   │   │
│   │   │   ├── infrastructure/
│   │   │   │   ├── local.datasource.impl.ts
│   │   │   │   └── repository.impl.ts
│   │   │   │
│   │   │   └── presentation/
│   │   │       ├── controller.ts
│   │   │       └── routes.ts
│   │   │
│   │   ├── shared/
│   │   │   ├── domain/
│   │   │   │   ├── dtos/
│   │   │   │   ├── entities/
│   │   │   └── presentation/
│   │   │       └── middlewares/
│   │   │
│   │   ├── todos/
│   │   │   ├── domain/
│   │   │   │   ├── datasources/
│   │   │   │   ├── dtos/
│   │   │   │   ├── entities/
│   │   │   │   ├── repositories/
│   │   │   │   └── usecases/
│   │   │   │
│   │   │   ├── infrastructure/
│   │   │   │   ├── local.datasource.impl.ts
│   │   │   │   └── repository.impl.ts
│   │   │   │
│   │   │   └── presentation/
│   │   │       ├── controller.ts
│   │   │       └── routes.ts
│   │   └── ...
│   ├── app.test.ts
│   ├── app.ts
│   ├── routes.ts
│   ├── server.ts
│   └── testServer.ts
├── .env
├── .env.template
├── .env.test
├── ...
├── package.json
└── ...

Domain — Entities

Entities are objects that represent fundamental concepts of the application domain. These objects encapsulate the essential state and behavior of key elements within the system.

Domain — Repositories

Repositories are a data access abstraction that act as an interface between the domain layer and the infrastructure layer. Their primary purpose is to encapsulate the logic related to data storage and retrieval, providing an abstraction layer that allows the domain layer to work with entities without worrying about the specific details of how data is stored or retrieved.

Domain — Use cases

Use cases represent the specific actions or functionalities that can be performed by a user or a system within the application. These use cases encapsulate the business logic in a way that is independent of infrastructure and implementation details, making them portable and reusable in different contexts.

Domain — Data sources

Data sources are interfaces or abstractions that represent the data source from which the data needed for the application is obtained. These data sources can be databases, web services, file systems, or any other form of data storage. The use of data sources helps decouple business logic from the specific details of the data source. This means that the domain layer can work with data sources through generic interfaces without knowing the specific implementation details, making it easy to exchange or update the data source without affecting the application logic.

Domain — DTOs

DTOs (Data Transfer Objects) are objects that are used to transfer data between different layers of the application, especially between the presentation layer and the domain or infrastructure layer. DTOs encapsulate related data and transport it from one context to another without exposing the underlying business logic. The main function of DTOs is to represent information in a structured and coherent way, facilitating its transport through the application.

Infrastructure — Repository Implementation

The repository implementation at the infrastructure layer is responsible for providing a concrete implementation of the methods defined in the repository interface at the domain layer. This implementation is responsible for interacting with the actual data source, such as a database, an external service or any other data persistence mechanism.

Infrastructure — Data source Implementation

The data source implementation in the infrastructure layer is responsible for providing a concrete implementation of the methods defined in the data source interface in the domain layer. This component is responsible for interacting directly with the actual data source, such as a database, a web service or any other data storage medium.

Presentation— Controller

Controllers are presentation layer components that act as entry points for client requests in an application. These controllers are responsible for receiving HTTP requests, processing them and directing them to the corresponding business logic in the domain layer.

Presentation — Routes

Routes are presentation layer components that are responsible for defining routes and handling incoming HTTP requests to an application. These routes are used to map HTTP requests to the corresponding controllers and establish the API structure or routing of the application. It is also where our data source and our repository are initialized, the same that is necessary for our controller.


Implementing a REST API using Node.js, Express and following good development practices and Clean Architecture provides a solid foundation for developing modern and scalable web applications. By taking a modular approach and focusing on separation of concerns, developers can achieve a clean, maintainable architecture that encourages flexibility and continuous system evolution.

The application of Clean Architecture allows you to maintain a clear separation between the different layers of the application, such as the domain layer, the infrastructure layer, and the presentation layer, making it easier to understand and maintain the code over time. Additionally, adopting good development practices such as using middlewares for intermediate tasks, validating input data, and proper error handling contributes to creating a robust and secure API.


Stay in touch

node-template-server's People

Contributors

baguilar6174 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

node-template-server's Issues

separate validation from dto definition and using zod library for validation

hello and thanks for this clean code
but i see that you re implementing the dto validation inside the same class
my suggestion to use an external library like zod
to do the validation in separate class or layer for example:
1.create a schema directory inside domain directory
2.create a file auth.schema.ts inside it


export const registerUserSchema = object({
  body: object({
    name: string({
      required_error: 'Name is required',
    }),
    email: string({
      required_error: 'Email address is required',
    }).email('Invalid email address'),
    password: string({
      required_error: 'Password is required',
    })
      .min(8, 'Password must be more than 8 characters')
      .max(32, 'Password must be less than 32 characters'),
    passwordConfirm: string({
      required_error: 'Please confirm your password',
    }),
   
  }).refine((data) => data.password === data.passwordConfirm, {
    path: ['passwordConfirm'],
    message: 'Passwords do not match',
  }),
});

export const loginUserSchema = object({
  body: object({
    email: string({
      required_error: 'Email address is required',
    }).email('Invalid email address'),
    password: string({
      required_error: 'Password is required',
    }).min(8, 'Invalid email or password'),
  }),
});

export const verifyEmailSchema = object({
  params: object({
    verificationCode: string(),
  }),
});

export type RegisterUserInput = Omit<TypeOf<typeof registerUserSchema>['body'],'passwordConfirm'>;
export type LoginUserInput = TypeOf<typeof loginUserSchema>['body'];
export type VerifyEmailInput = TypeOf<typeof `verifyEmailSchema>['params'];

3.create a validation middleware in shared module

import { type Response, type NextFunction, type Request } from 'express';
import { AnyZodObject, ZodError } from 'zod';

export class ValidateMiddlewares {
	//* Dependency injection
	// constructor() {}

	public static validate = (schema: AnyZodObject) => (req: Request, res: Response, next: NextFunction) => {

		try {
			schema.parse({
				params: req.params,
				query: req.query,
				body: req.body,
			});

			next();
		} catch (error) {
			if (error instanceof ZodError) {
				return res.status(400).json({
					status: 'fail',
					errors: error.errors,
				});
			}
			next(error);
		}
	};
}

4.the login.dto.ts and register.dto.ts becomes

import { RegisterUserInput } from "../schemas";

/**
 * DTOs must have a validate method that throws an error
 * if the data is invalid or missing required fields.
 */
export class RegisterUserDto  {
	private constructor(
		public readonly name: string,
		public readonly email: string,
		public readonly password: string
	) {
		
	}


	public static create(input: RegisterUserInput): RegisterUserDto {
		const { name, email, password } = input;
		return new RegisterUserDto(name as string, email as string, password as string);
	}
}
import { LoginUserInput } from '../schemas';

/**
 * DTOs must have a validate method that throws an error
 * if the data is invalid or missing required fields.
 */
export class LoginUserDto  {
	private constructor(
		public readonly email: string,
		public readonly password: string
	) {
		
	}

	public static create(input: LoginUserInput): LoginUserDto {
		const { email, password } = input;
		return new LoginUserDto(email as string, password as string);
	}
}

5.auth controller.ts

import { HttpCode, type SuccessResponse } from '../../../core';
import {
	type AuthRepository,
	RegisterUserDto,
	LoginUser,
	type AuthEntity,
	RegisterUser,
	LoginUserDto
} from '../domain';
import { LoginUserInput, RegisterUserInput } from '../domain/schemas';


export class AuthController {
	//* Dependency injection
	constructor(private readonly repository: AuthRepository) { }

	public login = (
		req: Request<unknown, unknown, LoginUserInput>,
		res: Response<SuccessResponse<AuthEntity>>,
		next: NextFunction
	): void => {

		const dto = LoginUserDto.create(req.body);
		new LoginUser(this.repository)
			.execute(dto)
			.then((result) => res.json({ data: result }))
			.catch(next);
	};

	public register = (
		req: Request<unknown, unknown, RegisterUserInput>,
		res: Response<SuccessResponse<AuthEntity>>,
		next: NextFunction
	): void => {
		const dto = RegisterUserDto.create(req.body);
		new RegisterUser(this.repository)
			.execute(dto)
			.then((result) => res.status(HttpCode.CREATED).json({ data: result }))
			.catch(next);
	};
}

6.auth routes

import { Router } from 'express';

import { AuthController } from './controller';
import { AuthDatasourceImpl, AuthRepositoryImpl } from '../infrastructure';
import { ValidateMiddlewares } from '../../shared';
import { loginUserSchema, registerUserSchema } from '../domain/schemas';

export class AuthRoutes {
	static get routes(): Router {
		const router = Router();

		const datasource = new AuthDatasourceImpl();
		const repository = new AuthRepositoryImpl(datasource);
		const controller = new AuthController(repository);


		router.post('/login',ValidateMiddlewares.validate(loginUserSchema) ,controller.login);
		router.post('/register',ValidateMiddlewares.validate(registerUserSchema), controller.register);

		return router;
	}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.