{"id":26885308,"url":"https://github.com/fawzymokhtar/typescript-in-nodejs-starter","last_synced_at":"2025-05-08T22:30:48.385Z","repository":{"id":41689589,"uuid":"247979921","full_name":"FawzyMokhtar/TypeScript-in-Nodejs-Starter","owner":"FawzyMokhtar","description":"A starter kit for Node.js project written with TypeScript.","archived":false,"fork":false,"pushed_at":"2022-12-12T05:46:27.000Z","size":703,"stargazers_count":38,"open_issues_count":4,"forks_count":15,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T18:52:29.812Z","etag":null,"topics":["eslint","express","express-validator","husky","json-api","lint-staged","mysql","nodejs","postgresql","prettier","sequelize","typescript","winston"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FawzyMokhtar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-17T13:41:23.000Z","updated_at":"2023-10-23T11:11:20.000Z","dependencies_parsed_at":"2023-01-27T14:31:15.015Z","dependency_job_id":null,"html_url":"https://github.com/FawzyMokhtar/TypeScript-in-Nodejs-Starter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FawzyMokhtar%2FTypeScript-in-Nodejs-Starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FawzyMokhtar%2FTypeScript-in-Nodejs-Starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FawzyMokhtar%2FTypeScript-in-Nodejs-Starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FawzyMokhtar%2FTypeScript-in-Nodejs-Starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FawzyMokhtar","download_url":"https://codeload.github.com/FawzyMokhtar/TypeScript-in-Nodejs-Starter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253157581,"owners_count":21863137,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eslint","express","express-validator","husky","json-api","lint-staged","mysql","nodejs","postgresql","prettier","sequelize","typescript","winston"],"created_at":"2025-03-31T18:52:33.882Z","updated_at":"2025-05-08T22:30:48.338Z","avatar_url":"https://github.com/FawzyMokhtar.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Typescript in Nodejs Starter Kit\n\nA starter kit for \u003cb\u003e[Node.js](https://nodejs.org/)\u003c/b\u003e project written with \u003cb\u003e[TypeScript](https://www.typescriptlang.org/)\u003c/b\u003e.\n\nThis project is well organized, scalable and maintainable boilerplate as your application's business grows.\n\n## Prerequisites\n\nIt's is recommended before start to have a basic knowledge about the following\n\n- [TypeScript](https://www.typescriptlang.org/).\n- [Express](https://www.npmjs.com/package/express).\n- [Express Validator](https://www.npmjs.com/package/express-validator).\n- [Json API](https://jsonapi.org/) with [examples](https://jsonapi.org/examples/) (optional).\n\n## Table of contents\n\n- [Problem definition](#problem-definition).\n- [How to solve the problem](#how-to-solve-the-problem).\n- [Getting Started](#getting-started)\n  - [Setup project dependencies](#setup-project-dependencies).\n  - [Run project](#to-run-this-project-in-development-environment-run-the-command).\n- [Project structure](#project-structure).\n- [Database](#database) (In-memory Database).\n- [API](#api)\n  - [App specific error codes](#app-specific-error-codes).\n  - [API examples](#api-examples).\n- [Static code analysis](#static-code-analysis).\n\n## Problem definition\n\nAs we know \u003cb\u003eJavaScript\u003c/b\u003e doesn't enforce type checking by itself, this may not be a problem when developing small \u003cb\u003eNode.js\u003c/b\u003e apps, but it will be a \u003cb\u003e\u003ci\u003eBIG problem\u003c/i\u003e\u003c/b\u003e when building a multi-module or scaled apps.\n\n## How to solve the problem\n\nSince \u003cb\u003eTypeScript\u003c/b\u003e is a super set of \u003cb\u003eJavaScript\u003c/b\u003e, we can have \u003cb\u003eNode.js\u003c/b\u003e apps to be written using \u003cb\u003eTyeScript\u003c/b\u003e.\n\n\u003cb\u003eTypeScript\u003c/b\u003e is transpiled using \u003cb\u003eTypeScript Compiler\u003c/b\u003e - also known as \u003cb\u003etsc\u003c/b\u003e - which can be adjusted to output the desired version of \u003cb\u003eECMAScript\u003c/b\u003e.\n\n## Getting Started\n\n### Setup project dependencies\n\n- If you have \u003cb\u003eyarn\u003c/b\u003e installed on your machine:\n\n  Open your terminal on the project's root folder \u0026 run the following command\n\n  ```bash\n  yarn\n  ```\n\n- If you don't have \u003cb\u003eyarn\u003c/b\u003e installed on your machine:\n\n  Open your terminal on the project's root folder \u0026 run the following command\n\n  ```bash\n  npm i -g yarn\n  ```\n\n  then\n\n  ```bash\n   yarn\n  ```\n\n### To run this project in development environment run the command\n\n```bash\nyarn run dev\n```\n\n### To build this project in run the command\n\n```bash\nyarn run build\n```\n\n### To run this project from the built output run the command\n\n```bash\nyarn run start\n```\n\n## Project structure\n\n### Overview\n\nWe are building a simple electronics store with to basic entities\n\n1. Category.\n2. Product.\n\nEach Category will have the properties\n\n- `id: number` (🔑 primary key).\n- `name: string`.\n\nEach Product will have the properties\n\n- `id: number` (🔑 primary key).\n- `name: string`.\n- `price: number`.\n- `categoryId: number` (🗝 foreign key from category).\n\n### Folder structure\n\n```bash\n./src\n└── app\n    ├── app.ts\n    ├── categories\n    │   ├── data\n    │   ├── models\n    │   └── validation\n    ├── controllers\n    ├── products\n    │   ├── data\n    │   ├── models\n    │   └── validation\n    ├── shared\n    │   ├── db\n    │   │   └── db.json\n    │   ├── middleware\n    │   ├── models\n    │   └── utils\n    └── startup\n        └── server.ts\n```\n\n- \u003cspan style=\"color: blue\"\u003eapp.ts\u003c/span\u003e is the entry point of the application.\n\n- \u003cspan style=\"color: blue\"\u003estartup/server.ts\u003c/span\u003e file is where \u003cb\u003eNode.js\u003c/b\u003e server options getting set.\n\n- Each folder on the \u003cspan style=\"color: blue\"\u003eapp\u003c/span\u003e folder represents an application \u003cspan style=\"color: blue\"\u003emodule\u003c/span\u003e.\n\n- \u003cspan style=\"color: blue\"\u003eshared module\u003c/span\u003e contains \u003cspan style=\"color: blue\"\u003eutilities\u003c/span\u003e and \u003cspan style=\"color: blue\"\u003eshared models\u003c/span\u003e which will be used by other application modules.\n\n- \u003cspan style=\"color: blue\"\u003econtrollers module\u003c/span\u003e is where all application modules' routes are being defined.\n\n- Each module may contains\n\n  - \u003cspan style=\"color: blue\"\u003edata\u003c/span\u003e folder to define its data-access functionalities.\n  - \u003cspan style=\"color: blue\"\u003emodels\u003c/span\u003e folder in which the module models are defined.\n  - \u003cspan style=\"color: blue\"\u003evalidation\u003c/span\u003e folder in which all models validations are defined, e.g. validating a model for creating a category.\n  - Each folder must has a \u003cspan style=\"color: blue\"\u003eindex.ts\u003c/span\u003e by a convention to export each (\u003cspan style=\"color: blue\"\u003eclass\u003c/span\u003e, \u003cspan style=\"color: blue\"\u003einterface\u003c/span\u003e, \u003cspan style=\"color: blue\"\u003efunction\u003c/span\u003e \u0026 etc..) defined in this folder.\n\n### Database\n\nUntil now this boilerplate supports five types of databases:\n\n1. In-memory Database (current branch].\n2. PostgreSQL Database (branch [postgresql-integration](https://github.com/FawzyMokhtar/TypeScript-in-Nodejs-Starter/tree/postgresql-integration)).\n3. MySQL Database (branch [mysql-integration](https://github.com/FawzyMokhtar/TypeScript-in-Nodejs-Starter/tree/mysql-integration)).\n4. MSSQL Database ((branch [mssql-integration](https://github.com/FawzyMokhtar/TypeScript-in-Nodejs-Starter/tree/mssql-integration)).\n5. MongoDB Database ((branch [mongodb-integration](https://github.com/FawzyMokhtar/TypeScript-in-Nodejs-Starter/tree/mongodb-integration)).\n\nWe are using a \u003cspan style=\"color: blue\"\u003ejson\u003c/span\u003e file as virtual database.\n\nThe database file could be found in the location \u003cspan style=\"color: blue\"\u003e./src/app/shared/db/db.json\u003c/span\u003e .\n\n\u003cb\u003eImportant note \u003c/b\u003e\u003cspan style=\"background-color: #2697EC;color: #ffff; font-size: 1.25rem;\"\u003e↴\u003c/span\u003e\n\nWhen we load the \u003cspan style=\"color: blue\"\u003edb.json\u003c/span\u003e file we create an \u003cb style=\"color: blue\"\u003ein-memory\u003c/b\u003e database which means are changes such as (create, update \u0026 delete) category or product will be available until the application is restarted.\n\nWe use a class called \u003cb\u003eDatabase\u003c/b\u003e defined in the file \u003cspan style=\"color: blue\"\u003e./src/app/shared/models/database.model.ts\u003c/span\u003e to load and query the data from the \u003cspan style=\"color: blue\"\u003edb.json\u003c/span\u003e file.\n\n```typescript\n/**\n * Represents a virtual database with two tables `categories` and `products`.\n */\nexport class Database {\n  /**\n   * Gets or sets the set of categories available in the database.\n   */\n  public categories: Category[] = [];\n\n  /**\n   * Gets or sets the set of products available in the database.\n   */\n  public products: Product[] = [];\n\n  /**\n   * Creates a new instance of @see Database and loads the database data.\n   */\n  public static async connect(): Promise\u003cDatabase\u003e {\n    return ((await import('../db/db.json')) as unknown) as Database;\n  }\n}\n```\n\n### API\n\nSince we follow the [Json API](https://jsonapi.org/) standards, our web api should always return a response with this structure\n\n```typescript\n/**\n * Represents an application http-response's body in case of success or even failure.\n */\nexport interface AppHttpResponse {\n  /**\n   * Gets or sets the data that requested or created by the user.\n   *\n   * This property will has a value only if the request was succeeded.\n   */\n  data?: unknown;\n\n  /**\n   * Gets or sets the metadata for the http response.\n   */\n  meta?: AppHttpResponseMeta;\n\n  /**\n   * Gets or sets a set of errors that occurs during request processing.\n   *\n   * @summary e.g. validation errors, security errors or even internal server errors.\n   */\n  errors?: AppHttpResponseError[];\n}\n```\n\nWhere the `meta` has the structure\n\n```typescript\n/**\n * Represents an application http-response metadata.\n */\nexport interface AppHttpResponseMeta {\n  /**\n   * Gets or sets the current pagination page.\n   */\n  page?: number;\n\n  /**\n   * Gets or sets the maximum allowed items per-page.\n   */\n  pageSize?: number;\n\n  /**\n   * Gets or sets the count of the actual items in the current page.\n   */\n  count?: number;\n\n  /**\n   * Gets or sets the total count of items available in the database those match the query criteria.\n   */\n  total?: number;\n\n  /**\n   * Gets or sets the number of the previous pagination page.\n   */\n  previousPage?: number | undefined;\n\n  /**\n   * Gets or sets the number of the next pagination page.\n   */\n  nextPage?: number | undefined;\n\n  /**\n   * Gets or sets the total available pagination pages those match the query criteria.\n   */\n  totalPages?: number;\n}\n```\n\nAnd each error in the `errors` property has to be\n\n```typescript\n/**\n * Represents an app http error that should be sent within a failed request's response.\n *\n * @summary All error members are optional but the more details the server sends back to the client the more easy it becomes to fix the error.\n */\nexport interface AppHttpResponseError {\n  /**\n   * Gets or sets the application-specific code for this error.\n   */\n  code?: AppErrorCode;\n\n  /**\n   * Gets or sets the name of the source that causes this error.\n   *\n   * Usually it's the name of the property that causes the error.\n   *\n   * The property maybe a nested property,\n   * in this case use e.g. if we are validating a `Person` object use `address.postalCode` instead of `postalCode`.\n   */\n  source?: string;\n\n  /**\n   * Gets or sets a generic title of the problem.\n   */\n  title?: string;\n\n  /**\n   * Gets or sets a more descriptive details for the problem, unlike the generic @field title.\n   */\n  detail?: string;\n}\n```\n\n#### App specific error codes\n\nThe code member of an error object contains an application-specific code representing the type of problem encountered. code is similar to title in that both identify a general type of problem (unlike detail, which is specific to the particular instance of the problem), but dealing with code is easier programmatically, because the “same” title may appear in different forms due to localization as described [here](https://jsonapi.org/examples/#error-objects-error-codes).\n\nOur application-specific codes are defined in an `Enum` called `AppErrorCode` with the following definition\n\n```typescript\n/**\n * The application-specific error codes.\n */\nexport enum AppErrorCode {\n  /** Un-authenticated code. */\n  UnAuthenticated = 1,\n\n  /** Access denied or forbidden code. */\n  Forbidden = 2,\n\n  /** Internal server code. */\n  InternalServerError = 3,\n\n  /** The field is required code. */\n  IsRequired = 4,\n\n  /** The field type is invalid. */\n  InvalidType = 5,\n\n  /** The field type is String and its length is invalid. */\n  InvalidLength = 6,\n\n  /** The entity field value already exists in another entity. */\n  ValueExists = 7,\n\n  /** The entity can't be deleted due to its existing relations with other entities. */\n  CantBeDeleted = 8,\n\n  /**\n   * The related entity isn't found,\n   * @summary e.g. you are trying to create a new product in a category which is not exists in the database.\n   */\n  RelatedEntityNotFound = 9\n}\n```\n\n### API examples\n\n- A \u003cspan style=\"color: green\"\u003esuccess\u003c/span\u003e response\n\n  Searching for a list of products those matching some criteria\n\n  `[GET]` `http://localhost:3000/api/products?name=Samsung\u0026categories=1,2,3\u0026page=1\u0026pageSize=3`\n\n  Should return a response like the following\n\n  ```json\n  {\n    \"data\": [\n      {\n        \"id\": 1,\n        \"name\": \"Samsung Galaxy S5\",\n        \"price\": 5000,\n        \"categoryId\": 1,\n        \"category\": {\n          \"id\": 1,\n          \"name\": \"Mobiles\"\n        }\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Samsung Galaxy S6\",\n        \"price\": 4500,\n        \"categoryId\": 1,\n        \"category\": {\n          \"id\": 1,\n          \"name\": \"Mobiles\"\n        }\n      },\n      {\n        \"id\": 15,\n        \"name\": \"Samsung 32 Inch HD LED Standard TV - UA32K4000\",\n        \"price\": 3550,\n        \"categoryId\": 3,\n        \"category\": {\n          \"id\": 3,\n          \"name\": \"TVs\"\n        }\n      }\n    ],\n    \"meta\": {\n      \"page\": 1,\n      \"pageSize\": 3,\n      \"count\": 3,\n      \"total\": 3,\n      \"totalPages\": 1\n    }\n  }\n  ```\n\n- A \u003cspan style=\"color: #FF9966\"\u003ebad-request\u003c/span\u003e response\n\n  Try to create a new product with a name that is already exists in the database\n\n  `[POST]` `http://localhost:3000/api/products`\n\n  ```json\n  {\n    \"name\": \"Samsung Galaxy S5\",\n    \"price\": 12300.0,\n    \"categoryId\": 1\n  }\n  ```\n\n  Should return a response like the following\n\n  ```json\n  {\n    \"errors\": [\n      {\n        \"code\": 7,\n        \"source\": \"name\",\n        \"title\": \"Field value already exists\",\n        \"detail\": \"Product name already exists\"\n      }\n    ]\n  }\n  ```\n\n- The \u003cspan style=\"color: red\"\u003einternal server error\u003c/span\u003e, \u003cspan style=\"color: #cc0\"\u003eun-authenticated\u003c/span\u003e and \u003cspan style=\"color: #ffcc00\"\u003eforbidden\u003c/span\u003e responses should follow the same convention.\n\n## Static code analysis\n\nWe are using the following plugins to statically analysis our code\n\n- [ESLint](https://eslint.org/).\n- [Prettier](https://prettier.io/).\n- [Husky](https://www.npmjs.com/package/husky).\n- [Lint-Staged](https://www.npmjs.com/package/lint-staged).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffawzymokhtar%2Ftypescript-in-nodejs-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffawzymokhtar%2Ftypescript-in-nodejs-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffawzymokhtar%2Ftypescript-in-nodejs-starter/lists"}