{"id":89370,"date":"2024-12-22T14:30:49","date_gmt":"2024-12-22T11:00:49","guid":{"rendered":"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/"},"modified":"2024-12-22T14:30:49","modified_gmt":"2024-12-22T11:00:49","slug":"functional-programming-with-fp-ts-in-nodejs-3239","status":"publish","type":"post","link":"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/","title":{"rendered":"\u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0648\u06cc\u0633\u06cc \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc \u0628\u0627 fp-ts \u062f\u0631 Node.js"},"content":{"rendered":"<p>Summarize this content to 400 words in Persian Lang <\/p>\n<p>  \u0645\u0642\u062f\u0645\u0647<\/p>\n<p>\u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0648\u06cc\u0633\u06cc \u062a\u0627\u0628\u0639\u06cc (FP) \u0628\u0647 \u062f\u0644\u06cc\u0644 \u062a\u0631\u06a9\u06cc\u0628 \u067e\u0630\u06cc\u0631\u06cc\u060c \u0622\u0632\u0645\u0627\u06cc\u0634 \u067e\u0630\u06cc\u0631\u06cc \u0648 \u0627\u0633\u062a\u062d\u06a9\u0627\u0645 \u0622\u0646 \u0645\u062d\u0628\u0648\u0628\u06cc\u062a \u067e\u06cc\u062f\u0627 \u06a9\u0631\u062f\u0647 \u0627\u0633\u062a. \u062f\u0631 \u0627\u06a9\u0648\u0633\u06cc\u0633\u062a\u0645 \u062c\u0627\u0648\u0627 \u0627\u0633\u06a9\u0631\u06cc\u067e\u062a\u060c \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0647\u0627 \u0645\u0627\u0646\u0646\u062f fp-ts \u0645\u0641\u0627\u0647\u06cc\u0645 \u0642\u062f\u0631\u062a\u0645\u0646\u062f FP \u0631\u0627 \u0628\u0647 TypeScript \u0628\u06cc\u0627\u0648\u0631\u06cc\u062f \u0648 \u0628\u0647 \u0634\u0645\u0627 \u0627\u0645\u06a9\u0627\u0646 \u0645\u06cc \u062f\u0647\u062f \u06a9\u062f\u0647\u0627\u06cc \u062a\u0645\u06cc\u0632\u062a\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0627\u0639\u062a\u0645\u0627\u062f\u062a\u0631\u06cc \u0628\u0646\u0648\u06cc\u0633\u06cc\u062f.<\/p>\n<p>\u0627\u06cc\u0646 \u0645\u0642\u0627\u0644\u0647 \u0628\u0631\u0631\u0633\u06cc \u0645\u06cc \u06a9\u0646\u062f fp-ts \u0645\u0641\u0627\u0647\u06cc\u0645\u06cc \u0645\u0627\u0646\u0646\u062f Option\u060c Either\u060c Task\u060c Reader\u060c \u0648 ReaderTaskEither. \u0645\u0627 \u0628\u0627 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u0648\u0644\u06cc\u0647 CRUD \u062e\u0648\u0627\u0647\u06cc\u0645 \u0633\u0627\u062e\u062a fp-ts\u060c \u0635\u0641\u062d\u0647 (\u06a9\u0644\u0627\u06cc\u0646\u062a PostgreSQL)\u060c \u0648 Express.js \u0628\u0631\u0627\u06cc \u062f\u06cc\u062f\u0646 \u0627\u06cc\u0646\u06a9\u0647 \u0686\u06af\u0648\u0646\u0647 \u0627\u06cc\u0646 \u0627\u0646\u062a\u0632\u0627\u0639\u0627\u062a \u062f\u0631 \u06a9\u0627\u0631\u0628\u0631\u062f\u0647\u0627\u06cc \u062f\u0646\u06cc\u0627\u06cc \u0648\u0627\u0642\u0639\u06cc \u0645\u06cc \u062f\u0631\u062e\u0634\u0646\u062f.<\/p>\n<p>  \u0645\u0641\u0627\u0647\u06cc\u0645 \u06a9\u0644\u06cc\u062f\u06cc<\/p>\n<p>\u0642\u0628\u0644 \u0627\u0632 \u0648\u0631\u0648\u062f \u0628\u0647 \u0628\u0631\u0646\u0627\u0645\u0647\u060c \u0627\u062c\u0627\u0632\u0647 \u062f\u0647\u06cc\u062f \u0628\u0647 \u0637\u0648\u0631 \u062e\u0644\u0627\u0635\u0647 \u062f\u0631 \u0645\u0648\u0631\u062f \u0645\u0641\u0627\u0647\u06cc\u0645 \u0627\u0635\u0644\u06cc \u0628\u062d\u062b \u06a9\u0646\u06cc\u0645:<\/p>\n<p>\u06af\u0632\u06cc\u0646\u0647: \u0648\u062c\u0648\u062f \u06cc\u0627 \u0639\u062f\u0645 \u0648\u062c\u0648\u062f \u06cc\u06a9 \u0645\u0642\u062f\u0627\u0631 \u0631\u0627 \u0645\u062f\u0644 \u0645\u06cc \u06a9\u0646\u062f (Some \u06cc\u0627 None).<\/p>\n<p>\u0647\u0631 \u062f\u0648: \u0646\u0634\u0627\u0646 \u062f\u0647\u0646\u062f\u0647 \u0645\u062d\u0627\u0633\u0628\u0627\u062a\u06cc \u0627\u0633\u062a \u06a9\u0647 \u0645\u06cc \u062a\u0648\u0627\u0646\u0646\u062f \u0645\u0648\u0641\u0642 \u0634\u0648\u0646\u062f (Right) \u06cc\u0627 \u0634\u06a9\u0633\u062a \u0628\u062e\u0648\u0631\u062f (Left).<\/p>\n<p>\u0648\u0638\u06cc\u0641\u0647: \u0646\u0634\u0627\u0646 \u062f\u0647\u0646\u062f\u0647 \u0645\u062d\u0627\u0633\u0628\u0627\u062a \u0646\u0627\u0647\u0645\u0632\u0645\u0627\u0646 \u062a\u0646\u0628\u0644 \u0627\u0633\u062a.<\/p>\n<p>\u062e\u0648\u0627\u0646\u0646\u062f\u0647: \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627 \u0631\u0627 \u0628\u0647 \u0645\u062d\u0627\u0633\u0628\u0627\u062a \u062a\u0632\u0631\u06cc\u0642 \u0645\u06cc \u06a9\u0646\u062f.<\/p>\n<p>ReaderTaskEither: Reader\u060c Task \u0648 Either \u0631\u0627 \u0628\u0631\u0627\u06cc \u0639\u0645\u0644\u06cc\u0627\u062a \u0646\u0627\u0647\u0645\u06af\u0627\u0645 \u0628\u0627 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627 \u0648 \u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627 \u062a\u0631\u06a9\u06cc\u0628 \u0645\u06cc \u06a9\u0646\u062f.<\/p>\n<p>  \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u067e\u0631\u0648\u0698\u0647<\/p>\n<p>  \u067e\u0631\u0648\u0698\u0647 \u0631\u0627 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u06a9\u0646\u06cc\u062f<\/p>\n<p>mkdir fp-ts-crud &amp;&amp; cd fp-ts-crud<br \/>\nnpm init -y<br \/>\nnpm install express pg fp-ts io-ts<br \/>\nnpm install &#8211;save-dev typescript @types\/express ts-node-dev jest @types\/jest ts-jest<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u062a\u0646\u0638\u06cc\u0645 TypeScript<\/p>\n<p>\u0627\u06cc\u062c\u0627\u062f \u06cc\u06a9 tsconfig.json:<\/p>\n<p>{<br \/>\n  &#8220;compilerOptions&#8221;: {<br \/>\n    &#8220;target&#8221;: &#8220;ES2020&#8221;,<br \/>\n    &#8220;module&#8221;: &#8220;CommonJS&#8221;,<br \/>\n    &#8220;outDir&#8221;: &#8220;dist&#8221;,<br \/>\n    &#8220;strict&#8221;: true,<br \/>\n    &#8220;esModuleInterop&#8221;: true<br \/>\n  },<br \/>\n  &#8220;include&#8221;: [&#8220;src\/**\/*&#8221;]\n}<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0633\u0627\u062e\u062a\u0627\u0631 \u067e\u0631\u0648\u0698\u0647<\/p>\n<p>src\/<br \/>\n  index.ts        # Entry point<br \/>\n  db.ts           # Database setup<br \/>\n  models\/         # Data models and validation<br \/>\n  services\/       # Business logic<br \/>\n  controllers\/    # CRUD operations<br \/>\n  utils\/          # fp-ts utilities<br \/>\n  errors\/         # Custom error classes<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u067e\u06cc\u0627\u062f\u0647 \u0633\u0627\u0632\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 CRUD<\/p>\n<p>  \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u067e\u0627\u06cc\u06af\u0627\u0647 \u062f\u0627\u062f\u0647 (db.ts)<\/p>\n<p>import { Pool } from &#8216;pg&#8217;;<\/p>\n<p>export const pool = new Pool({<br \/>\n  user: &#8216;postgres&#8217;,<br \/>\n  host: &#8216;localhost&#8217;,<br \/>\n  database: &#8216;fp_ts_crud&#8217;,<br \/>\n  password: &#8216;password&#8217;,<br \/>\n  port: 5432,<br \/>\n});<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u062a\u0639\u0631\u06cc\u0641 \u0645\u062f\u0644 \u0647\u0627 \u0648 \u0627\u0639\u062a\u0628\u0627\u0631\u0633\u0646\u062c\u06cc (models\/User.ts)<\/p>\n<p>import * as t from &#8216;io-ts&#8217;;<br \/>\nimport { isRight } from &#8216;fp-ts\/Either&#8217;;<\/p>\n<p>export const User = t.type({<br \/>\n  id: t.number,<br \/>\n  name: t.string,<br \/>\n  email: t.string,<br \/>\n});<\/p>\n<p>export const validateUser = (data: unknown): t.TypeOf&lt;typeof User&gt; | null =&gt; {<br \/>\n  const result = User.decode(data);<br \/>\n  return isRight(result) ? result.right : null;<br \/>\n};<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627\u06cc \u0633\u0641\u0627\u0631\u0634\u06cc (errors\/AppError.ts)<\/p>\n<p>export class AppError extends Error {<br \/>\n  constructor(public statusCode: number, public code: string, public message: string) {<br \/>\n    super(message);<br \/>\n    this.name = &#8216;AppError&#8217;;<br \/>\n  }<br \/>\n}<\/p>\n<p>export const createAppError = (statusCode: number, code: string, message: string): AppError =&gt; {<br \/>\n  return new AppError(statusCode, code, message);<br \/>\n};<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0644\u0627\u06cc\u0647 \u0633\u0631\u0648\u06cc\u0633 (services\/UserService.ts)<\/p>\n<p>import { pool } from &#8216;..\/db&#8217;;<br \/>\nimport { ReaderTaskEither, right, left } from &#8216;fp-ts\/ReaderTaskEither&#8217;;<br \/>\nimport { pipe } from &#8216;fp-ts\/function&#8217;;<br \/>\nimport { createAppError, AppError } from &#8216;..\/errors\/AppError&#8217;;<br \/>\nimport { validateUser } from &#8216;..\/models\/User&#8217;;<\/p>\n<p>type Dependencies = { db: typeof pool };<br \/>\ntype User = { name: string; email: string };<\/p>\n<p>export const createUser = (<br \/>\n  user: User<br \/>\n): ReaderTaskEither&lt;Dependencies, AppError, string&gt; =&gt; (deps) =&gt; async () =&gt; {<br \/>\n  \/\/ Validate the incoming user data<br \/>\n  const validatedUser = validateUser(user);<\/p>\n<p>  if (!validatedUser) {<br \/>\n    return left(createAppError(400, &#8216;INVALID_USER_DATA&#8217;, &#8216;Invalid user data provided&#8217;));<br \/>\n  }<\/p>\n<p>  try {<br \/>\n    const result = await deps.db.query(<br \/>\n      &#8216;INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id&#8217;,<br \/>\n      [validatedUser.name, validatedUser.email]\n    );<br \/>\n    return right(`User created with ID: ${result.rows[0].id}`);<br \/>\n  } catch (error) {<br \/>\n    return left(createAppError(500, &#8216;USER_CREATION_FAILED&#8217;, &#8216;Failed to create user&#8217;));<br \/>\n  }<br \/>\n};<\/p>\n<p>export const getUser = (<br \/>\n  id: number<br \/>\n): ReaderTaskEither&lt;Dependencies, AppError, { id: number; name: string; email: string }&gt; =&gt; (deps) =&gt; async () =&gt; {<br \/>\n  try {<br \/>\n    const result = await deps.db.query(&#8216;SELECT * FROM users WHERE id = $1&#8217;, [id]);<br \/>\n    return result.rows[0]\n      ? right(result.rows[0])<br \/>\n      : left(createAppError(404, &#8216;USER_NOT_FOUND&#8217;, &#8216;User not found&#8217;));<br \/>\n  } catch {<br \/>\n    return left(createAppError(500, &#8216;USER_FETCH_FAILED&#8217;, &#8216;Failed to fetch user&#8217;));<br \/>\n  }<br \/>\n};<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0639\u0645\u0644\u06cc\u0627\u062a CRUD (controllers\/UserController.ts)<\/p>\n<p>import { pipe } from &#8216;fp-ts\/function&#8217;;<br \/>\nimport { createUser, getUser } from &#8216;..\/services\/UserService&#8217;;<br \/>\nimport { pool } from &#8216;..\/db&#8217;;<br \/>\nimport { AppError } from &#8216;..\/errors\/AppError&#8217;;<\/p>\n<p>const errorHandler = (err: unknown, res: express.Response): void =&gt; {<br \/>\n  if (err instanceof AppError) {<br \/>\n    res.status(err.statusCode).json({ error: { code: err.code, message: err.message } });<br \/>\n  } else {<br \/>\n    res.status(500).json({ error: { code: &#8216;UNKNOWN_ERROR&#8217;, message: &#8216;An unexpected error occurred&#8217; } });<br \/>\n  }<br \/>\n};<\/p>\n<p>export const createUserHandler = (req: express.Request, res: express.Response): void =&gt; {<br \/>\n  pipe(<br \/>\n    createUser(req.body),<br \/>\n    (task) =&gt; task({ db: pool }),<br \/>\n    (promise) =&gt;<br \/>\n      promise.then((result) =&gt;<br \/>\n        result._tag === &#8216;Left&#8217;<br \/>\n          ? errorHandler(result.left, res)<br \/>\n          : res.json({ message: result.right })<br \/>\n      )<br \/>\n  );<br \/>\n};<\/p>\n<p>export const getUserHandler = (req: express.Request, res: express.Response): void =&gt; {<br \/>\n  pipe(<br \/>\n    getUser(parseInt(req.params.id, 10)),<br \/>\n    (task) =&gt; task({ db: pool }),<br \/>\n    (promise) =&gt;<br \/>\n      promise.then((result) =&gt;<br \/>\n        result._tag === &#8216;Left&#8217;<br \/>\n          ? errorHandler(result.left, res)<br \/>\n          : res.json(result.right)<br \/>\n      )<br \/>\n  );<br \/>\n};<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  Express API (index.ts)<\/p>\n<p>import express from &#8216;express&#8217;;<br \/>\nimport { createUserHandler, getUserHandler } from &#8216;.\/controllers\/UserController&#8217;;<\/p>\n<p>const app = express();<br \/>\napp.use(express.json());<\/p>\n<p>\/\/ Routes<br \/>\napp.post(&#8216;\/users&#8217;, createUserHandler);<br \/>\napp.get(&#8216;\/users\/:id&#8217;, getUserHandler);<\/p>\n<p>\/\/ Start Server<br \/>\napp.listen(3000, () =&gt; {<br \/>\n  console.log(&#8216;Server running on http:\/\/localhost:3000&#8217;);<br \/>\n});<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0627\u062c\u0631\u0627\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0628\u0627 Docker \u0648 Docker Compose<\/p>\n<p>  Dockerfile<\/p>\n<p># Stage 1: Build<br \/>\nFROM node:22 AS builder<br \/>\nWORKDIR \/app<br \/>\nCOPY package*.json .<br \/>\nRUN npm install<br \/>\nCOPY . .<br \/>\nRUN npm run build<\/p>\n<p># Stage 2: Run<br \/>\nFROM node:22<br \/>\nWORKDIR \/app<br \/>\nCOPY &#8211;from=builder \/app\/dist .\/dist<br \/>\nCOPY package*.json .\/<br \/>\nRUN npm install &#8211;production<br \/>\nCMD [&#8220;node&#8221;, &#8220;dist\/index.js&#8221;]\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  docker-compose.yml<\/p>\n<p>version: &#8216;3.8&#8217;<br \/>\nservices:<br \/>\n  db:<br \/>\n    image: postgres:15<br \/>\n    environment:<br \/>\n      POSTGRES_USER: postgres<br \/>\n      POSTGRES_PASSWORD: password<br \/>\n      POSTGRES_DB: fp_ts_crud<br \/>\n    ports:<br \/>\n      &#8211; &#8220;5432:5432&#8221;<br \/>\n    volumes:<br \/>\n      &#8211; db_data:\/var\/lib\/postgresql\/data<br \/>\nvolumes:<br \/>\n  db_data:<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0628\u0631\u0646\u0627\u0645\u0647 &#8211; \u062d\u0627\u0644\u062a \u062a\u0648\u0633\u0639\u0647 \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u062f<\/p>\n<p># Start the database<br \/>\ndocker-compose up -d<\/p>\n<p># Run the app<br \/>\nnpx ts-node-dev src\/index.ts<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0628\u0631\u0646\u0627\u0645\u0647 &#8211; \u062d\u0627\u0644\u062a \u062a\u0648\u0644\u06cc\u062f \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u062f<\/p>\n<p># Build the docker image<br \/>\ndocker build -t fp-ts-crud-app .<\/p>\n<p># Start the database<br \/>\ndocker-compose up -d<\/p>\n<p># Run the container<br \/>\ndocker run -p 3000:3000 fp-ts-crud-app<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u062a\u0633\u062a \u0647\u0627\u06cc \u0646\u0648\u0634\u062a\u0627\u0631\u06cc<\/p>\n<p>  \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0628\u0644\u0647<\/p>\n<p>\u0628\u0647 \u0631\u0648\u0632 \u0631\u0633\u0627\u0646\u06cc package.json \u0627\u0633\u06a9\u0631\u06cc\u067e\u062a \u0647\u0627:<\/p>\n<p>&#8220;scripts&#8221;: {<br \/>\n  &#8220;test&#8221;: &#8220;jest&#8221;,<br \/>\n  &#8220;test:watch&#8221;: &#8220;jest &#8211;watch&#8221;<br \/>\n}<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0622\u0632\u0645\u0648\u0646 \u0646\u0645\u0648\u0646\u0647 (__tests__\/UserService.test.ts)<\/p>\n<p>import { createUser, getUser } from &#8216;..\/services\/UserService&#8217;;<br \/>\nimport { pool } from &#8216;..\/db&#8217;;<\/p>\n<p>jest.mock(&#8216;..\/db&#8217;, () =&gt; ({<br \/>\n  pool: {<br \/>\n    query: jest.fn(),<br \/>\n  },<br \/>\n}));<\/p>\n<p>describe(&#8216;UserService&#8217;, () =&gt; {<br \/>\n  afterEach(() =&gt; {<br \/>\n    jest.clearAllMocks();<br \/>\n  });<\/p>\n<p>  it(&#8216;should create a user&#8217;, async () =&gt; {<br \/>\n    (pool.query as jest.Mock).mockResolvedValueOnce({ rows: [{ id: 1 }] });<\/p>\n<p>    const result = await createUser({ name: &#8216;Alice&#8217;, email: &#8216;alice@example.com&#8217; })({ db: pool })();<\/p>\n<p>    expect(result._tag).toBe(&#8216;Right&#8217;);<br \/>\n    if (result._tag === &#8216;Right&#8217;) {<br \/>\n      expect(result.right).toBe(&#8216;User created with ID: 1&#8217;);<br \/>\n    }<br \/>\n  });<\/p>\n<p>  it(&#8216;should return error if user not found&#8217;, async () =&gt; {<br \/>\n    (pool.query as jest.Mock).mockResolvedValueOnce({ rows: [] });<\/p>\n<p>    const result = await getUser(1)({ db: pool })();<\/p>\n<p>    expect(result._tag).toBe(&#8216;Left&#8217;);<br \/>\n    if (result._tag === &#8216;Left&#8217;) {<br \/>\n      expect(result.left.message).toBe(&#8216;User not found&#8217;);<br \/>\n    }<br \/>\n  });<\/p>\n<p>  it(&#8216;should return error for invalid user data during creation&#8217;, async () =&gt; {<br \/>\n    const invalidData = { name: 123, email: &#8216;invalid-email&#8217; };<br \/>\n    const result = await createUser(invalidData)({ db: pool })();<br \/>\n    expect(result._tag).toBe(&#8216;Left&#8217;);<\/p>\n<p>    if (result._tag === &#8216;Left&#8217;) {<br \/>\n      expect(result.left.message).toBe(&#8216;Invalid user data provided&#8217;);<br \/>\n    }<br \/>\n  });<br \/>\n});<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>  \u0646\u062a\u06cc\u062c\u0647 \u06af\u06cc\u0631\u06cc<\/p>\n<p>\u0628\u0627 \u0627\u0639\u0645\u0627\u0644 \u0627\u0647\u0631\u0645 fp-ts\u060c Docker \u0648 \u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627\u06cc \u0642\u0648\u06cc\u060c \u0645\u0627 \u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc Node.js CRUD\u060c \u0645\u0642\u06cc\u0627\u0633 \u067e\u0630\u06cc\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0646\u06af\u0647\u062f\u0627\u0631\u06cc \u0633\u0627\u062e\u062a\u06cc\u0645. \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0627\u0644\u06af\u0648\u0647\u0627\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0648\u06cc\u0633\u06cc \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc \u06a9\u062f \u0634\u0645\u0627 \u0631\u0627 \u0642\u0627\u0628\u0644 \u067e\u06cc\u0634 \u0628\u06cc\u0646\u06cc \u062a\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0627\u0639\u062a\u0645\u0627\u062f\u062a\u0631 \u0645\u06cc \u06a9\u0646\u062f\u060c \u0628\u0647 \u062e\u0635\u0648\u0635 \u062f\u0631 \u0647\u0646\u06af\u0627\u0645 \u0645\u062f\u06cc\u0631\u06cc\u062a \u06af\u0631\u062f\u0634 \u06a9\u0627\u0631 \u0646\u0627\u0647\u0645\u0632\u0645\u0627\u0646.<\/p>\n<div data-article-id=\"2169367\" id=\"article-body\">\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_85 counter-hierarchy ez-toc-counter-rtl ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">\u0641\u0647\u0631\u0633\u062a \u0645\u0637\u0627\u0644\u0628<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%85%D9%82%D8%AF%D9%85%D9%87\" >\u0645\u0642\u062f\u0645\u0647<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85_%DA%A9%D9%84%DB%8C%D8%AF%DB%8C\" >\u0645\u0641\u0627\u0647\u06cc\u0645 \u06a9\u0644\u06cc\u062f\u06cc<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%D9%BE%D8%B1%D9%88%DA%98%D9%87\" >\u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u067e\u0631\u0648\u0698\u0647<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%BE%D8%B1%D9%88%DA%98%D9%87_%D8%B1%D8%A7_%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%DA%A9%D9%86%DB%8C%D8%AF\" >\u067e\u0631\u0648\u0698\u0647 \u0631\u0627 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u06a9\u0646\u06cc\u062f<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%AA%D9%86%D8%B8%DB%8C%D9%85_TypeScript\" >\u062a\u0646\u0638\u06cc\u0645 TypeScript<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1_%D9%BE%D8%B1%D9%88%DA%98%D9%87\" >\u0633\u0627\u062e\u062a\u0627\u0631 \u067e\u0631\u0648\u0698\u0647<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%BE%DB%8C%D8%A7%D8%AF%D9%87_%D8%B3%D8%A7%D8%B2%DB%8C_%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_CRUD\" >\u067e\u06cc\u0627\u062f\u0647 \u0633\u0627\u0632\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 CRUD<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87_%D8%AF%D8%A7%D8%AF%D9%87_dbts\" >\u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u067e\u0627\u06cc\u06af\u0627\u0647 \u062f\u0627\u062f\u0647 (db.ts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%AA%D8%B9%D8%B1%DB%8C%D9%81_%D9%85%D8%AF%D9%84_%D9%87%D8%A7_%D9%88_%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C_modelsUserts\" >\u062a\u0639\u0631\u06cc\u0641 \u0645\u062f\u0644 \u0647\u0627 \u0648 \u0627\u0639\u062a\u0628\u0627\u0631\u0633\u0646\u062c\u06cc (models\/User.ts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA_%D8%AE%D8%B7%D8%A7%DB%8C_%D8%B3%D9%81%D8%A7%D8%B1%D8%B4%DB%8C_errorsAppErrorts\" >\u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627\u06cc \u0633\u0641\u0627\u0631\u0634\u06cc (errors\/AppError.ts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%84%D8%A7%DB%8C%D9%87_%D8%B3%D8%B1%D9%88%DB%8C%D8%B3_servicesUserServicets\" >\u0644\u0627\u06cc\u0647 \u0633\u0631\u0648\u06cc\u0633 (services\/UserService.ts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA_CRUD_controllersUserControllerts\" >\u0639\u0645\u0644\u06cc\u0627\u062a CRUD (controllers\/UserController.ts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#Express_API_indexts\" >Express API (index.ts)<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C_%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_%D8%A8%D8%A7_Docker_%D9%88_Docker_Compose\" >\u0627\u062c\u0631\u0627\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0628\u0627 Docker \u0648 Docker Compose<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#Dockerfile\" >Dockerfile<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#docker-composeyml\" >docker-compose.yml<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_%E2%80%93_%D8%AD%D8%A7%D9%84%D8%AA_%D8%AA%D9%88%D8%B3%D8%B9%D9%87_%D8%B1%D8%A7_%D8%A7%D8%AC%D8%B1%D8%A7_%DA%A9%D9%86%DB%8C%D8%AF\" >\u0628\u0631\u0646\u0627\u0645\u0647 &#8211; \u062d\u0627\u0644\u062a \u062a\u0648\u0633\u0639\u0647 \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u062f<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_%E2%80%93_%D8%AD%D8%A7%D9%84%D8%AA_%D8%AA%D9%88%D9%84%DB%8C%D8%AF_%D8%B1%D8%A7_%D8%A7%D8%AC%D8%B1%D8%A7_%DA%A9%D9%86%DB%8C%D8%AF\" >\u0628\u0631\u0646\u0627\u0645\u0647 &#8211; \u062d\u0627\u0644\u062a \u062a\u0648\u0644\u06cc\u062f \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u062f<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%AA%D8%B3%D8%AA_%D9%87%D8%A7%DB%8C_%D9%86%D9%88%D8%B4%D8%AA%D8%A7%D8%B1%DB%8C\" >\u062a\u0633\u062a \u0647\u0627\u06cc \u0646\u0648\u0634\u062a\u0627\u0631\u06cc<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%D8%A8%D9%84%D9%87\" >\u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0628\u0644\u0647<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D8%A2%D8%B2%D9%85%D9%88%D9%86_%D9%86%D9%85%D9%88%D9%86%D9%87_tests_UserServicetestts\" >\u0622\u0632\u0645\u0648\u0646 \u0646\u0645\u0648\u0646\u0647 (__tests__\/UserService.test.ts)<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/nabfollower.com\/blog\/functional-programming-with-fp-ts-in-nodejs-3239\/#%D9%86%D8%AA%DB%8C%D8%AC%D9%87_%DA%AF%DB%8C%D8%B1%DB%8C\" >\u0646\u062a\u06cc\u062c\u0647 \u06af\u06cc\u0631\u06cc<\/a><\/li><\/ul><\/nav><\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D9%85%D9%82%D8%AF%D9%85%D9%87\"><\/span>\n<p>  \u0645\u0642\u062f\u0645\u0647<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p>\u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0648\u06cc\u0633\u06cc \u062a\u0627\u0628\u0639\u06cc (FP) \u0628\u0647 \u062f\u0644\u06cc\u0644 \u062a\u0631\u06a9\u06cc\u0628 \u067e\u0630\u06cc\u0631\u06cc\u060c \u0622\u0632\u0645\u0627\u06cc\u0634 \u067e\u0630\u06cc\u0631\u06cc \u0648 \u0627\u0633\u062a\u062d\u06a9\u0627\u0645 \u0622\u0646 \u0645\u062d\u0628\u0648\u0628\u06cc\u062a \u067e\u06cc\u062f\u0627 \u06a9\u0631\u062f\u0647 \u0627\u0633\u062a. \u062f\u0631 \u0627\u06a9\u0648\u0633\u06cc\u0633\u062a\u0645 \u062c\u0627\u0648\u0627 \u0627\u0633\u06a9\u0631\u06cc\u067e\u062a\u060c \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0647\u0627 \u0645\u0627\u0646\u0646\u062f <strong>fp-ts<\/strong> \u0645\u0641\u0627\u0647\u06cc\u0645 \u0642\u062f\u0631\u062a\u0645\u0646\u062f FP \u0631\u0627 \u0628\u0647 TypeScript \u0628\u06cc\u0627\u0648\u0631\u06cc\u062f \u0648 \u0628\u0647 \u0634\u0645\u0627 \u0627\u0645\u06a9\u0627\u0646 \u0645\u06cc \u062f\u0647\u062f \u06a9\u062f\u0647\u0627\u06cc \u062a\u0645\u06cc\u0632\u062a\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0627\u0639\u062a\u0645\u0627\u062f\u062a\u0631\u06cc \u0628\u0646\u0648\u06cc\u0633\u06cc\u062f.<\/p>\n<p>\u0627\u06cc\u0646 \u0645\u0642\u0627\u0644\u0647 \u0628\u0631\u0631\u0633\u06cc \u0645\u06cc \u06a9\u0646\u062f <strong>fp-ts<\/strong> \u0645\u0641\u0627\u0647\u06cc\u0645\u06cc \u0645\u0627\u0646\u0646\u062f <code>Option<\/code>\u060c <code>Either<\/code>\u060c <code>Task<\/code>\u060c <code>Reader<\/code>\u060c \u0648 <code>ReaderTaskEither<\/code>. \u0645\u0627 \u0628\u0627 \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u0648\u0644\u06cc\u0647 CRUD \u062e\u0648\u0627\u0647\u06cc\u0645 \u0633\u0627\u062e\u062a <strong>fp-ts<\/strong>\u060c <strong>\u0635\u0641\u062d\u0647<\/strong> (\u06a9\u0644\u0627\u06cc\u0646\u062a PostgreSQL)\u060c \u0648 <strong>Express.js<\/strong> \u0628\u0631\u0627\u06cc \u062f\u06cc\u062f\u0646 \u0627\u06cc\u0646\u06a9\u0647 \u0686\u06af\u0648\u0646\u0647 \u0627\u06cc\u0646 \u0627\u0646\u062a\u0632\u0627\u0639\u0627\u062a \u062f\u0631 \u06a9\u0627\u0631\u0628\u0631\u062f\u0647\u0627\u06cc \u062f\u0646\u06cc\u0627\u06cc \u0648\u0627\u0642\u0639\u06cc \u0645\u06cc \u062f\u0631\u062e\u0634\u0646\u062f.<\/p>\n<hr\/>\n<h3><span class=\"ez-toc-section\" id=\"%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85_%DA%A9%D9%84%DB%8C%D8%AF%DB%8C\"><\/span>\n<p>  \u0645\u0641\u0627\u0647\u06cc\u0645 \u06a9\u0644\u06cc\u062f\u06cc<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>\u0642\u0628\u0644 \u0627\u0632 \u0648\u0631\u0648\u062f \u0628\u0647 \u0628\u0631\u0646\u0627\u0645\u0647\u060c \u0627\u062c\u0627\u0632\u0647 \u062f\u0647\u06cc\u062f \u0628\u0647 \u0637\u0648\u0631 \u062e\u0644\u0627\u0635\u0647 \u062f\u0631 \u0645\u0648\u0631\u062f \u0645\u0641\u0627\u0647\u06cc\u0645 \u0627\u0635\u0644\u06cc \u0628\u062d\u062b \u06a9\u0646\u06cc\u0645:<\/p>\n<ol>\n<li>\n<strong>\u06af\u0632\u06cc\u0646\u0647<\/strong>: \u0648\u062c\u0648\u062f \u06cc\u0627 \u0639\u062f\u0645 \u0648\u062c\u0648\u062f \u06cc\u06a9 \u0645\u0642\u062f\u0627\u0631 \u0631\u0627 \u0645\u062f\u0644 \u0645\u06cc \u06a9\u0646\u062f (<code>Some<\/code> \u06cc\u0627 <code>None<\/code>).<\/li>\n<li>\n<strong>\u0647\u0631 \u062f\u0648<\/strong>: \u0646\u0634\u0627\u0646 \u062f\u0647\u0646\u062f\u0647 \u0645\u062d\u0627\u0633\u0628\u0627\u062a\u06cc \u0627\u0633\u062a \u06a9\u0647 \u0645\u06cc \u062a\u0648\u0627\u0646\u0646\u062f \u0645\u0648\u0641\u0642 \u0634\u0648\u0646\u062f (<code>Right<\/code>) \u06cc\u0627 \u0634\u06a9\u0633\u062a \u0628\u062e\u0648\u0631\u062f (<code>Left<\/code>).<\/li>\n<li>\n<strong>\u0648\u0638\u06cc\u0641\u0647<\/strong>: \u0646\u0634\u0627\u0646 \u062f\u0647\u0646\u062f\u0647 \u0645\u062d\u0627\u0633\u0628\u0627\u062a \u0646\u0627\u0647\u0645\u0632\u0645\u0627\u0646 \u062a\u0646\u0628\u0644 \u0627\u0633\u062a.<\/li>\n<li>\n<strong>\u062e\u0648\u0627\u0646\u0646\u062f\u0647<\/strong>: \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627 \u0631\u0627 \u0628\u0647 \u0645\u062d\u0627\u0633\u0628\u0627\u062a \u062a\u0632\u0631\u06cc\u0642 \u0645\u06cc \u06a9\u0646\u062f.<\/li>\n<li>\n<strong>ReaderTaskEither<\/strong>: Reader\u060c Task \u0648 Either \u0631\u0627 \u0628\u0631\u0627\u06cc \u0639\u0645\u0644\u06cc\u0627\u062a \u0646\u0627\u0647\u0645\u06af\u0627\u0645 \u0628\u0627 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627 \u0648 \u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627 \u062a\u0631\u06a9\u06cc\u0628 \u0645\u06cc \u06a9\u0646\u062f.<\/li>\n<\/ol>\n<hr\/>\n<h3><span class=\"ez-toc-section\" id=\"%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%D9%BE%D8%B1%D9%88%DA%98%D9%87\"><\/span>\n<p>  \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u067e\u0631\u0648\u0698\u0647<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"%D9%BE%D8%B1%D9%88%DA%98%D9%87_%D8%B1%D8%A7_%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%DA%A9%D9%86%DB%8C%D8%AF\"><\/span>\n<p>  \u067e\u0631\u0648\u0698\u0647 \u0631\u0627 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u06a9\u0646\u06cc\u062f<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code><span class=\"nb\">mkdir <\/span>fp-ts-crud <span class=\"o\">&amp;&amp;<\/span> <span class=\"nb\">cd <\/span>fp-ts-crud\nnpm init <span class=\"nt\">-y<\/span>\nnpm <span class=\"nb\">install <\/span>express pg fp-ts io-ts\nnpm <span class=\"nb\">install<\/span> <span class=\"nt\">--save-dev<\/span> typescript @types\/express ts-node-dev jest @types\/jest ts-jest\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%AA%D9%86%D8%B8%DB%8C%D9%85_TypeScript\"><\/span>\n<p>  \u062a\u0646\u0638\u06cc\u0645 TypeScript<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p>\u0627\u06cc\u062c\u0627\u062f \u06cc\u06a9 <code>tsconfig.json<\/code>:<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight json\"><code><span class=\"p\">{<\/span><span class=\"w\">\n  <\/span><span class=\"nl\">\"compilerOptions\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n    <\/span><span class=\"nl\">\"target\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"ES2020\"<\/span><span class=\"p\">,<\/span><span class=\"w\">\n    <\/span><span class=\"nl\">\"module\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"CommonJS\"<\/span><span class=\"p\">,<\/span><span class=\"w\">\n    <\/span><span class=\"nl\">\"outDir\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"dist\"<\/span><span class=\"p\">,<\/span><span class=\"w\">\n    <\/span><span class=\"nl\">\"strict\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">,<\/span><span class=\"w\">\n    <\/span><span class=\"nl\">\"esModuleInterop\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"w\">\n  <\/span><span class=\"p\">},<\/span><span class=\"w\">\n  <\/span><span class=\"nl\">\"include\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">[<\/span><span class=\"s2\">\"src\/**\/*\"<\/span><span class=\"p\">]<\/span><span class=\"w\">\n<\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1_%D9%BE%D8%B1%D9%88%DA%98%D9%87\"><\/span>\n<p>  \u0633\u0627\u062e\u062a\u0627\u0631 \u067e\u0631\u0648\u0698\u0647<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>src\/\n  index.ts        # Entry point\n  db.ts           # Database setup\n  models\/         # Data models and validation\n  services\/       # Business logic\n  controllers\/    # CRUD operations\n  utils\/          # fp-ts utilities\n  errors\/         # Custom error classes\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<hr\/>\n<h3><span class=\"ez-toc-section\" id=\"%D9%BE%DB%8C%D8%A7%D8%AF%D9%87_%D8%B3%D8%A7%D8%B2%DB%8C_%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_CRUD\"><\/span>\n<p>  \u067e\u06cc\u0627\u062f\u0647 \u0633\u0627\u0632\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 CRUD<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87_%D8%AF%D8%A7%D8%AF%D9%87_dbts\"><\/span>\n<p>  \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u067e\u0627\u06cc\u06af\u0627\u0647 \u062f\u0627\u062f\u0647 (<code>db.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">Pool<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">pg<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">pool<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nc\">Pool<\/span><span class=\"p\">({<\/span>\n  <span class=\"na\">user<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">postgres<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">host<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">localhost<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">database<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fp_ts_crud<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">password<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">password<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">port<\/span><span class=\"p\">:<\/span> <span class=\"mi\">5432<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%AA%D8%B9%D8%B1%DB%8C%D9%81_%D9%85%D8%AF%D9%84_%D9%87%D8%A7_%D9%88_%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C_modelsUserts\"><\/span>\n<p>  \u062a\u0639\u0631\u06cc\u0641 \u0645\u062f\u0644 \u0647\u0627 \u0648 \u0627\u0639\u062a\u0628\u0627\u0631\u0633\u0646\u062c\u06cc (<code>models\/User.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">t<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">io-ts<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">isRight<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fp-ts\/Either<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">User<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">t<\/span><span class=\"p\">.<\/span><span class=\"nf\">type<\/span><span class=\"p\">({<\/span>\n  <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"nx\">t<\/span><span class=\"p\">.<\/span><span class=\"kr\">number<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"nx\">t<\/span><span class=\"p\">.<\/span><span class=\"kr\">string<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">email<\/span><span class=\"p\">:<\/span> <span class=\"nx\">t<\/span><span class=\"p\">.<\/span><span class=\"kr\">string<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">validateUser<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">data<\/span><span class=\"p\">:<\/span> <span class=\"nx\">unknown<\/span><span class=\"p\">):<\/span> <span class=\"nx\">t<\/span><span class=\"p\">.<\/span><span class=\"nx\">TypeOf<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">User<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">|<\/span> <span class=\"kc\">null<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">User<\/span><span class=\"p\">.<\/span><span class=\"nf\">decode<\/span><span class=\"p\">(<\/span><span class=\"nx\">data<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nf\">isRight<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">)<\/span> <span class=\"p\">?<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">right<\/span> <span class=\"p\">:<\/span> <span class=\"kc\">null<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA_%D8%AE%D8%B7%D8%A7%DB%8C_%D8%B3%D9%81%D8%A7%D8%B1%D8%B4%DB%8C_errorsAppErrorts\"><\/span>\n<p>  \u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627\u06cc \u0633\u0641\u0627\u0631\u0634\u06cc (<code>errors\/AppError.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">export<\/span> <span class=\"kd\">class<\/span> <span class=\"nc\">AppError<\/span> <span class=\"kd\">extends<\/span> <span class=\"nc\">Error<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nf\">constructor<\/span><span class=\"p\">(<\/span><span class=\"k\">public<\/span> <span class=\"nx\">statusCode<\/span><span class=\"p\">:<\/span> <span class=\"kr\">number<\/span><span class=\"p\">,<\/span> <span class=\"k\">public<\/span> <span class=\"nx\">code<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">,<\/span> <span class=\"k\">public<\/span> <span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">super<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">AppError<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createAppError<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">statusCode<\/span><span class=\"p\">:<\/span> <span class=\"kr\">number<\/span><span class=\"p\">,<\/span> <span class=\"nx\">code<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">,<\/span> <span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">):<\/span> <span class=\"nx\">AppError<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"k\">new<\/span> <span class=\"nc\">AppError<\/span><span class=\"p\">(<\/span><span class=\"nx\">statusCode<\/span><span class=\"p\">,<\/span> <span class=\"nx\">code<\/span><span class=\"p\">,<\/span> <span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D9%84%D8%A7%DB%8C%D9%87_%D8%B3%D8%B1%D9%88%DB%8C%D8%B3_servicesUserServicets\"><\/span>\n<p>  \u0644\u0627\u06cc\u0647 \u0633\u0631\u0648\u06cc\u0633 (<code>services\/UserService.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/db<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">ReaderTaskEither<\/span><span class=\"p\">,<\/span> <span class=\"nx\">right<\/span><span class=\"p\">,<\/span> <span class=\"nx\">left<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fp-ts\/ReaderTaskEither<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">pipe<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fp-ts\/function<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createAppError<\/span><span class=\"p\">,<\/span> <span class=\"nx\">AppError<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/errors\/AppError<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">validateUser<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/models\/User<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">Dependencies<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"na\">db<\/span><span class=\"p\">:<\/span> <span class=\"k\">typeof<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">};<\/span>\n<span class=\"kd\">type<\/span> <span class=\"nx\">User<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span> <span class=\"nl\">email<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span> <span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createUser<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span>\n  <span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">User<\/span>\n<span class=\"p\">):<\/span> <span class=\"nx\">ReaderTaskEither<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">Dependencies<\/span><span class=\"p\">,<\/span> <span class=\"nx\">AppError<\/span><span class=\"p\">,<\/span> <span class=\"kr\">string<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nx\">deps<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ Validate the incoming user data<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">validatedUser<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">validateUser<\/span><span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"k\">if <\/span><span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">validatedUser<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nf\">left<\/span><span class=\"p\">(<\/span><span class=\"nf\">createAppError<\/span><span class=\"p\">(<\/span><span class=\"mi\">400<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">INVALID_USER_DATA<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Invalid user data provided<\/span><span class=\"dl\">'<\/span><span class=\"p\">));<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">try<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">deps<\/span><span class=\"p\">.<\/span><span class=\"nx\">db<\/span><span class=\"p\">.<\/span><span class=\"nf\">query<\/span><span class=\"p\">(<\/span>\n      <span class=\"dl\">'<\/span><span class=\"s1\">INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n      <span class=\"p\">[<\/span><span class=\"nx\">validatedUser<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span><span class=\"p\">,<\/span> <span class=\"nx\">validatedUser<\/span><span class=\"p\">.<\/span><span class=\"nx\">email<\/span><span class=\"p\">]<\/span>\n    <span class=\"p\">);<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nf\">right<\/span><span class=\"p\">(<\/span><span class=\"s2\">`User created with ID: <\/span><span class=\"p\">${<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">rows<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">);<\/span>\n  <span class=\"p\">}<\/span> <span class=\"k\">catch <\/span><span class=\"p\">(<\/span><span class=\"nx\">error<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nf\">left<\/span><span class=\"p\">(<\/span><span class=\"nf\">createAppError<\/span><span class=\"p\">(<\/span><span class=\"mi\">500<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">USER_CREATION_FAILED<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Failed to create user<\/span><span class=\"dl\">'<\/span><span class=\"p\">));<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">getUser<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span>\n  <span class=\"nx\">id<\/span><span class=\"p\">:<\/span> <span class=\"kr\">number<\/span>\n<span class=\"p\">):<\/span> <span class=\"nx\">ReaderTaskEither<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">Dependencies<\/span><span class=\"p\">,<\/span> <span class=\"nx\">AppError<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"kr\">number<\/span><span class=\"p\">;<\/span> <span class=\"nl\">name<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span> <span class=\"nl\">email<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span> <span class=\"p\">}<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nx\">deps<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">try<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">deps<\/span><span class=\"p\">.<\/span><span class=\"nx\">db<\/span><span class=\"p\">.<\/span><span class=\"nf\">query<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">SELECT * FROM users WHERE id = $1<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span><span class=\"nx\">id<\/span><span class=\"p\">]);<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">rows<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>\n      <span class=\"p\">?<\/span> <span class=\"nf\">right<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">rows<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">])<\/span>\n      <span class=\"p\">:<\/span> <span class=\"nf\">left<\/span><span class=\"p\">(<\/span><span class=\"nf\">createAppError<\/span><span class=\"p\">(<\/span><span class=\"mi\">404<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">USER_NOT_FOUND<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">User not found<\/span><span class=\"dl\">'<\/span><span class=\"p\">));<\/span>\n  <span class=\"p\">}<\/span> <span class=\"k\">catch<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nf\">left<\/span><span class=\"p\">(<\/span><span class=\"nf\">createAppError<\/span><span class=\"p\">(<\/span><span class=\"mi\">500<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">USER_FETCH_FAILED<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Failed to fetch user<\/span><span class=\"dl\">'<\/span><span class=\"p\">));<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA_CRUD_controllersUserControllerts\"><\/span>\n<p>  \u0639\u0645\u0644\u06cc\u0627\u062a CRUD (<code>controllers\/UserController.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">pipe<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fp-ts\/function<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createUser<\/span><span class=\"p\">,<\/span> <span class=\"nx\">getUser<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/services\/UserService<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/db<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">AppError<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/errors\/AppError<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">errorHandler<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">:<\/span> <span class=\"nx\">unknown<\/span><span class=\"p\">,<\/span> <span class=\"nx\">res<\/span><span class=\"p\">:<\/span> <span class=\"nx\">express<\/span><span class=\"p\">.<\/span><span class=\"nx\">Response<\/span><span class=\"p\">):<\/span> <span class=\"k\">void<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">if <\/span><span class=\"p\">(<\/span><span class=\"nx\">err<\/span> <span class=\"k\">instanceof<\/span> <span class=\"nx\">AppError<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">res<\/span><span class=\"p\">.<\/span><span class=\"nf\">status<\/span><span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">.<\/span><span class=\"nx\">statusCode<\/span><span class=\"p\">).<\/span><span class=\"nf\">json<\/span><span class=\"p\">({<\/span> <span class=\"na\">error<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"na\">code<\/span><span class=\"p\">:<\/span> <span class=\"nx\">err<\/span><span class=\"p\">.<\/span><span class=\"nx\">code<\/span><span class=\"p\">,<\/span> <span class=\"na\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">err<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span> <span class=\"p\">}<\/span> <span class=\"p\">});<\/span>\n  <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">res<\/span><span class=\"p\">.<\/span><span class=\"nf\">status<\/span><span class=\"p\">(<\/span><span class=\"mi\">500<\/span><span class=\"p\">).<\/span><span class=\"nf\">json<\/span><span class=\"p\">({<\/span> <span class=\"na\">error<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"na\">code<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">UNKNOWN_ERROR<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"na\">message<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">An unexpected error occurred<\/span><span class=\"dl\">'<\/span> <span class=\"p\">}<\/span> <span class=\"p\">});<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createUserHandler<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">req<\/span><span class=\"p\">:<\/span> <span class=\"nx\">express<\/span><span class=\"p\">.<\/span><span class=\"nx\">Request<\/span><span class=\"p\">,<\/span> <span class=\"nx\">res<\/span><span class=\"p\">:<\/span> <span class=\"nx\">express<\/span><span class=\"p\">.<\/span><span class=\"nx\">Response<\/span><span class=\"p\">):<\/span> <span class=\"k\">void<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nf\">pipe<\/span><span class=\"p\">(<\/span>\n    <span class=\"nf\">createUser<\/span><span class=\"p\">(<\/span><span class=\"nx\">req<\/span><span class=\"p\">.<\/span><span class=\"nx\">body<\/span><span class=\"p\">),<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">task<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nf\">task<\/span><span class=\"p\">({<\/span> <span class=\"na\">db<\/span><span class=\"p\">:<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">}),<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">promise<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n      <span class=\"nx\">promise<\/span><span class=\"p\">.<\/span><span class=\"nf\">then<\/span><span class=\"p\">((<\/span><span class=\"nx\">result<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n        <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Left<\/span><span class=\"dl\">'<\/span>\n          <span class=\"p\">?<\/span> <span class=\"nf\">errorHandler<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">left<\/span><span class=\"p\">,<\/span> <span class=\"nx\">res<\/span><span class=\"p\">)<\/span>\n          <span class=\"p\">:<\/span> <span class=\"nx\">res<\/span><span class=\"p\">.<\/span><span class=\"nf\">json<\/span><span class=\"p\">({<\/span> <span class=\"na\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">right<\/span> <span class=\"p\">})<\/span>\n      <span class=\"p\">)<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">getUserHandler<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">req<\/span><span class=\"p\">:<\/span> <span class=\"nx\">express<\/span><span class=\"p\">.<\/span><span class=\"nx\">Request<\/span><span class=\"p\">,<\/span> <span class=\"nx\">res<\/span><span class=\"p\">:<\/span> <span class=\"nx\">express<\/span><span class=\"p\">.<\/span><span class=\"nx\">Response<\/span><span class=\"p\">):<\/span> <span class=\"k\">void<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nf\">pipe<\/span><span class=\"p\">(<\/span>\n    <span class=\"nf\">getUser<\/span><span class=\"p\">(<\/span><span class=\"nf\">parseInt<\/span><span class=\"p\">(<\/span><span class=\"nx\">req<\/span><span class=\"p\">.<\/span><span class=\"nx\">params<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span> <span class=\"mi\">10<\/span><span class=\"p\">)),<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">task<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nf\">task<\/span><span class=\"p\">({<\/span> <span class=\"na\">db<\/span><span class=\"p\">:<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">}),<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">promise<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n      <span class=\"nx\">promise<\/span><span class=\"p\">.<\/span><span class=\"nf\">then<\/span><span class=\"p\">((<\/span><span class=\"nx\">result<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n        <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Left<\/span><span class=\"dl\">'<\/span>\n          <span class=\"p\">?<\/span> <span class=\"nf\">errorHandler<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">left<\/span><span class=\"p\">,<\/span> <span class=\"nx\">res<\/span><span class=\"p\">)<\/span>\n          <span class=\"p\">:<\/span> <span class=\"nx\">res<\/span><span class=\"p\">.<\/span><span class=\"nf\">json<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">right<\/span><span class=\"p\">)<\/span>\n      <span class=\"p\">)<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"Express_API_indexts\"><\/span>\n<p>  Express API (<code>index.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">import<\/span> <span class=\"nx\">express<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">express<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createUserHandler<\/span><span class=\"p\">,<\/span> <span class=\"nx\">getUserHandler<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/controllers\/UserController<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">app<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">express<\/span><span class=\"p\">();<\/span>\n<span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nf\">use<\/span><span class=\"p\">(<\/span><span class=\"nx\">express<\/span><span class=\"p\">.<\/span><span class=\"nf\">json<\/span><span class=\"p\">());<\/span>\n\n<span class=\"c1\">\/\/ Routes<\/span>\n<span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nf\">post<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">\/users<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">createUserHandler<\/span><span class=\"p\">);<\/span>\n<span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nf\">get<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">\/users\/:id<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">getUserHandler<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ Start Server<\/span>\n<span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nf\">listen<\/span><span class=\"p\">(<\/span><span class=\"mi\">3000<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nf\">log<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Server running on http:\/\/localhost:3000<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<hr\/>\n<h3><span class=\"ez-toc-section\" id=\"%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C_%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_%D8%A8%D8%A7_Docker_%D9%88_Docker_Compose\"><\/span>\n<p>  \u0627\u062c\u0631\u0627\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0628\u0627 Docker \u0648 Docker Compose<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"Dockerfile\"><\/span>\n<p>  Dockerfile<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight docker\"><code><span class=\"c\"># Stage 1: Build<\/span>\n<span class=\"k\">FROM<\/span><span class=\"w\"> <\/span><span class=\"s\">node:22<\/span><span class=\"w\"> <\/span><span class=\"k\">AS<\/span><span class=\"w\"> <\/span><span class=\"s\">builder<\/span>\n<span class=\"k\">WORKDIR<\/span><span class=\"s\"> \/app<\/span>\n<span class=\"k\">COPY<\/span><span class=\"s\"> package*.json .<\/span>\n<span class=\"k\">RUN <\/span>npm <span class=\"nb\">install<\/span>\n<span class=\"k\">COPY<\/span><span class=\"s\"> . .<\/span>\n<span class=\"k\">RUN <\/span>npm run build\n\n<span class=\"c\"># Stage 2: Run<\/span>\n<span class=\"k\">FROM<\/span><span class=\"s\"> node:22<\/span>\n<span class=\"k\">WORKDIR<\/span><span class=\"s\"> \/app<\/span>\n<span class=\"k\">COPY<\/span><span class=\"s\"> --from=builder \/app\/dist .\/dist<\/span>\n<span class=\"k\">COPY<\/span><span class=\"s\"> package*.json .\/<\/span>\n<span class=\"k\">RUN <\/span>npm <span class=\"nb\">install<\/span> <span class=\"nt\">--production<\/span>\n<span class=\"k\">CMD<\/span><span class=\"s\"> [\"node\", \"dist\/index.js\"]<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"docker-composeyml\"><\/span>\n<p>  docker-compose.yml<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight yaml\"><code><span class=\"na\">version<\/span><span class=\"pi\">:<\/span> <span class=\"s1\">'<\/span><span class=\"s\">3.8'<\/span>\n<span class=\"na\">services<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">db<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">image<\/span><span class=\"pi\">:<\/span> <span class=\"s\">postgres:15<\/span>\n    <span class=\"na\">environment<\/span><span class=\"pi\">:<\/span>\n      <span class=\"na\">POSTGRES_USER<\/span><span class=\"pi\">:<\/span> <span class=\"s\">postgres<\/span>\n      <span class=\"na\">POSTGRES_PASSWORD<\/span><span class=\"pi\">:<\/span> <span class=\"s\">password<\/span>\n      <span class=\"na\">POSTGRES_DB<\/span><span class=\"pi\">:<\/span> <span class=\"s\">fp_ts_crud<\/span>\n    <span class=\"na\">ports<\/span><span class=\"pi\">:<\/span>\n      <span class=\"pi\">-<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">5432:5432\"<\/span>\n    <span class=\"na\">volumes<\/span><span class=\"pi\">:<\/span>\n      <span class=\"pi\">-<\/span> <span class=\"s\">db_data:\/var\/lib\/postgresql\/data<\/span>\n<span class=\"na\">volumes<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">db_data<\/span><span class=\"pi\">:<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_%E2%80%93_%D8%AD%D8%A7%D9%84%D8%AA_%D8%AA%D9%88%D8%B3%D8%B9%D9%87_%D8%B1%D8%A7_%D8%A7%D8%AC%D8%B1%D8%A7_%DA%A9%D9%86%DB%8C%D8%AF\"><\/span>\n<p>  \u0628\u0631\u0646\u0627\u0645\u0647 &#8211; \u062d\u0627\u0644\u062a \u062a\u0648\u0633\u0639\u0647 \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u062f<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code><span class=\"c\"># Start the database<\/span>\ndocker-compose up <span class=\"nt\">-d<\/span>\n\n<span class=\"c\"># Run the app<\/span>\nnpx ts-node-dev src\/index.ts\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87_%E2%80%93_%D8%AD%D8%A7%D9%84%D8%AA_%D8%AA%D9%88%D9%84%DB%8C%D8%AF_%D8%B1%D8%A7_%D8%A7%D8%AC%D8%B1%D8%A7_%DA%A9%D9%86%DB%8C%D8%AF\"><\/span>\n<p>  \u0628\u0631\u0646\u0627\u0645\u0647 &#8211; \u062d\u0627\u0644\u062a \u062a\u0648\u0644\u06cc\u062f \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u062f<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight shell\"><code><span class=\"c\"># Build the docker image<\/span>\ndocker build <span class=\"nt\">-t<\/span> fp-ts-crud-app <span class=\"nb\">.<\/span>\n\n<span class=\"c\"># Start the database<\/span>\ndocker-compose up <span class=\"nt\">-d<\/span>\n\n<span class=\"c\"># Run the container<\/span>\ndocker run <span class=\"nt\">-p<\/span> 3000:3000 fp-ts-crud-app\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<hr\/>\n<h3><span class=\"ez-toc-section\" id=\"%D8%AA%D8%B3%D8%AA_%D9%87%D8%A7%DB%8C_%D9%86%D9%88%D8%B4%D8%AA%D8%A7%D8%B1%DB%8C\"><\/span>\n<p>  \u062a\u0633\u062a \u0647\u0627\u06cc \u0646\u0648\u0634\u062a\u0627\u0631\u06cc<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"%D8%B1%D8%A7%D9%87_%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C_%D8%A8%D9%84%D9%87\"><\/span>\n<p>  \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0628\u0644\u0647<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p>\u0628\u0647 \u0631\u0648\u0632 \u0631\u0633\u0627\u0646\u06cc <code>package.json<\/code> \u0627\u0633\u06a9\u0631\u06cc\u067e\u062a \u0647\u0627:<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight json\"><code><span class=\"nl\">\"scripts\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n  <\/span><span class=\"nl\">\"test\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"jest\"<\/span><span class=\"p\">,<\/span><span class=\"w\">\n  <\/span><span class=\"nl\">\"test:watch\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"jest --watch\"<\/span><span class=\"w\">\n<\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<h4><span class=\"ez-toc-section\" id=\"%D8%A2%D8%B2%D9%85%D9%88%D9%86_%D9%86%D9%85%D9%88%D9%86%D9%87_tests_UserServicetestts\"><\/span>\n<p>  \u0622\u0632\u0645\u0648\u0646 \u0646\u0645\u0648\u0646\u0647 (<code>__tests__\/UserService.test.ts<\/code>)<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight typescript\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createUser<\/span><span class=\"p\">,<\/span> <span class=\"nx\">getUser<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/services\/UserService<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/db<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nf\">mock<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">..\/db<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n  <span class=\"na\">pool<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">query<\/span><span class=\"p\">:<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nf\">fn<\/span><span class=\"p\">(),<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">}));<\/span>\n\n<span class=\"nf\">describe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">UserService<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nf\">afterEach<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nf\">clearAllMocks<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">should create a user<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">pool<\/span><span class=\"p\">.<\/span><span class=\"nx\">query<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">Mock<\/span><span class=\"p\">).<\/span><span class=\"nf\">mockResolvedValueOnce<\/span><span class=\"p\">({<\/span> <span class=\"na\">rows<\/span><span class=\"p\">:<\/span> <span class=\"p\">[{<\/span> <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span> <span class=\"p\">}]<\/span> <span class=\"p\">});<\/span>\n\n    <span class=\"kd\">const<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nf\">createUser<\/span><span class=\"p\">({<\/span> <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Alice<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"na\">email<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">alice@example.com<\/span><span class=\"dl\">'<\/span> <span class=\"p\">})({<\/span> <span class=\"na\">db<\/span><span class=\"p\">:<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">})();<\/span>\n\n    <span class=\"nf\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span><span class=\"p\">).<\/span><span class=\"nf\">toBe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Right<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">if <\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Right<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nf\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">right<\/span><span class=\"p\">).<\/span><span class=\"nf\">toBe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">User created with ID: 1<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">should return error if user not found<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">pool<\/span><span class=\"p\">.<\/span><span class=\"nx\">query<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">Mock<\/span><span class=\"p\">).<\/span><span class=\"nf\">mockResolvedValueOnce<\/span><span class=\"p\">({<\/span> <span class=\"na\">rows<\/span><span class=\"p\">:<\/span> <span class=\"p\">[]<\/span> <span class=\"p\">});<\/span>\n\n    <span class=\"kd\">const<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nf\">getUser<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)({<\/span> <span class=\"na\">db<\/span><span class=\"p\">:<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">})();<\/span>\n\n    <span class=\"nf\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span><span class=\"p\">).<\/span><span class=\"nf\">toBe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Left<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">if <\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Left<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nf\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">left<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">).<\/span><span class=\"nf\">toBe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">User not found<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">should return error for invalid user data during creation<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">invalidData<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"mi\">123<\/span><span class=\"p\">,<\/span> <span class=\"na\">email<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">invalid-email<\/span><span class=\"dl\">'<\/span> <span class=\"p\">};<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nf\">createUser<\/span><span class=\"p\">(<\/span><span class=\"nx\">invalidData<\/span><span class=\"p\">)({<\/span> <span class=\"na\">db<\/span><span class=\"p\">:<\/span> <span class=\"nx\">pool<\/span> <span class=\"p\">})();<\/span>\n    <span class=\"nf\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span><span class=\"p\">).<\/span><span class=\"nf\">toBe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Left<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n    <span class=\"k\">if <\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">_tag<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Left<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nf\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">left<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">).<\/span><span class=\"nf\">toBe<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Invalid user data provided<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">});<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<hr\/>\n<h3><span class=\"ez-toc-section\" id=\"%D9%86%D8%AA%DB%8C%D8%AC%D9%87_%DA%AF%DB%8C%D8%B1%DB%8C\"><\/span>\n<p>  \u0646\u062a\u06cc\u062c\u0647 \u06af\u06cc\u0631\u06cc<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>\u0628\u0627 \u0627\u0639\u0645\u0627\u0644 \u0627\u0647\u0631\u0645 <strong>fp-ts<\/strong>\u060c Docker \u0648 \u0645\u062f\u06cc\u0631\u06cc\u062a \u062e\u0637\u0627\u06cc \u0642\u0648\u06cc\u060c \u0645\u0627 \u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc Node.js CRUD\u060c \u0645\u0642\u06cc\u0627\u0633 \u067e\u0630\u06cc\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0646\u06af\u0647\u062f\u0627\u0631\u06cc \u0633\u0627\u062e\u062a\u06cc\u0645. \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0627\u0644\u06af\u0648\u0647\u0627\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0648\u06cc\u0633\u06cc \u06a9\u0627\u0631\u0628\u0631\u062f\u06cc \u06a9\u062f \u0634\u0645\u0627 \u0631\u0627 \u0642\u0627\u0628\u0644 \u067e\u06cc\u0634 \u0628\u06cc\u0646\u06cc \u062a\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0627\u0639\u062a\u0645\u0627\u062f\u062a\u0631 \u0645\u06cc \u06a9\u0646\u062f\u060c \u0628\u0647 \u062e\u0635\u0648\u0635 \u062f\u0631 \u0647\u0646\u06af\u0627\u0645 \u0645\u062f\u06cc\u0631\u06cc\u062a \u06af\u0631\u062f\u0634 \u06a9\u0627\u0631 \u0646\u0627\u0647\u0645\u0632\u0645\u0627\u0646.<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Summarize this content to 400 words in Persian Lang \u0645\u0642\u062f\u0645\u0647 \u0628\u0631\u0646\u0627\u0645\u0647 \u0646\u0648\u06cc\u0633\u06cc \u062a\u0627\u0628\u0639\u06cc (FP) \u0628\u0647 \u062f\u0644\u06cc\u0644 \u062a\u0631\u06a9\u06cc\u0628 \u067e\u0630\u06cc\u0631\u06cc\u060c \u0622\u0632\u0645\u0627\u06cc\u0634 \u067e\u0630\u06cc\u0631\u06cc \u0648 \u0627\u0633\u062a\u062d\u06a9\u0627\u0645 \u0622\u0646 \u0645\u062d\u0628\u0648\u0628\u06cc\u062a \u067e\u06cc\u062f\u0627 \u06a9\u0631\u062f\u0647 \u0627\u0633\u062a. \u062f\u0631 \u0627\u06a9\u0648\u0633\u06cc\u0633\u062a\u0645 \u062c\u0627\u0648\u0627 \u0627\u0633\u06a9\u0631\u06cc\u067e\u062a\u060c \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0647\u0627 \u0645\u0627\u0646\u0646\u062f fp-ts \u0645\u0641\u0627\u0647\u06cc\u0645 \u0642\u062f\u0631\u062a\u0645\u0646\u062f FP \u0631\u0627 \u0628\u0647 TypeScript \u0628\u06cc\u0627\u0648\u0631\u06cc\u062f \u0648 \u0628\u0647 \u0634\u0645\u0627 \u0627\u0645\u06a9\u0627\u0646 \u0645\u06cc \u062f\u0647\u062f \u06a9\u062f\u0647\u0627\u06cc \u062a\u0645\u06cc\u0632\u062a\u0631 \u0648 \u0642\u0627\u0628\u0644 \u0627\u0639\u062a\u0645\u0627\u062f\u062a\u0631\u06cc \u0628\u0646\u0648\u06cc\u0633\u06cc\u062f. \u0627\u06cc\u0646 &hellip;<\/p>\n","protected":false},"author":2,"featured_media":89371,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"","fifu_image_alt":"","footnotes":""},"categories":[339],"tags":[],"class_list":["post-89370","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev"],"_links":{"self":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts\/89370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/comments?post=89370"}],"version-history":[{"count":0,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts\/89370\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/media\/89371"}],"wp:attachment":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/media?parent=89370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/categories?post=89370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/tags?post=89370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}