Heim > Web-Frontend > js-Tutorial > Funktionale Programmierung mit fp-ts in Node.js

Funktionale Programmierung mit fp-ts in Node.js

Linda Hamilton
Freigeben: 2024-12-31 01:08:17
Original
417 Leute haben es durchsucht

Functional Programming with fp-ts in Node.js

Einführung

Funktionale Programmierung (FP) erfreut sich aufgrund ihrer Zusammensetzbarkeit, Testbarkeit und Robustheit zunehmender Beliebtheit. Im JavaScript-Ökosystem bringen Bibliotheken wie fp-ts leistungsstarke FP-Konzepte in TypeScript ein, sodass Sie saubereren und zuverlässigeren Code schreiben können.

In diesem Artikel werden fp-ts-Konzepte wie Option, Entweder, Aufgabe, Reader und ReaderTaskEither untersucht. Wir erstellen eine einfache CRUD-App mit fp-ts, pg (PostgreSQL-Client) und Express.js, um zu sehen, wie diese Abstraktionen in der Realität glänzen. Weltanwendungen.


Schlüsselkonzepte

Bevor wir in die App eintauchen, besprechen wir kurz die Hauptkonzepte:

  1. Option: Modelliert das Vorhandensein oder Fehlen eines Werts (Einige oder Keiner).
  2. Entweder: Stellt Berechnungen dar, die erfolgreich (rechts) oder fehlschlagen (links) sein können.
  3. Aufgabe: Stellt verzögerte asynchrone Berechnungen dar.
  4. Reader: Fügt Abhängigkeiten in Berechnungen ein.
  5. ReaderTaskEither: Kombiniert Reader, Task und Both für asynchrone Vorgänge mit Abhängigkeiten und Fehlerbehandlung.

Einrichten des Projekts

Initialisieren Sie das Projekt

mkdir fp-ts-crud && cd fp-ts-crud
npm init -y
npm install express pg fp-ts io-ts
npm install --save-dev typescript @types/express ts-node-dev jest @types/jest ts-jest
Nach dem Login kopieren
Nach dem Login kopieren

TypeScript einrichten

Erstellen Sie eine tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"]
}
Nach dem Login kopieren
Nach dem Login kopieren

Projektstruktur

src/
  index.ts        # Entry point
  db.ts           # Database setup
  models/         # Data models and validation
  services/       # Business logic
  controllers/    # CRUD operations
  utils/          # fp-ts utilities
  errors/         # Custom error classes
Nach dem Login kopieren

Implementierung der CRUD-App

Datenbank-Setup (db.ts)

import { Pool } from 'pg';

export const pool = new Pool({
  user: 'postgres',
  host: 'localhost',
  database: 'fp_ts_crud',
  password: 'password',
  port: 5432,
});
Nach dem Login kopieren

Modelle und Validierung definieren (models/User.ts)

import * as t from 'io-ts';
import { isRight } from 'fp-ts/Either';

export const User = t.type({
  id: t.number,
  name: t.string,
  email: t.string,
});

export const validateUser = (data: unknown): t.TypeOf<typeof User> | null => {
  const result = User.decode(data);
  return isRight(result) ? result.right : null;
};
Nach dem Login kopieren

Benutzerdefinierte Fehlerbehandlung (errors/AppError.ts)

export class AppError extends Error {
  constructor(public statusCode: number, public code: string, public message: string) {
    super(message);
    this.name = 'AppError';
  }
}

export const createAppError = (statusCode: number, code: string, message: string): AppError => {
  return new AppError(statusCode, code, message);
};
Nach dem Login kopieren

Serviceschicht (services/UserService.ts)

import { pool } from '../db';
import { ReaderTaskEither, right, left } from 'fp-ts/ReaderTaskEither';
import { pipe } from 'fp-ts/function';
import { createAppError, AppError } from '../errors/AppError';

type Dependencies = { db: typeof pool };
type User = { name: string; email: string };

export const createUser = (
  user: User
): ReaderTaskEither<Dependencies, AppError, string> => (deps) => async () => {
  try {
    const result = await deps.db.query(
      'INSERT INTO users (name, email) VALUES (, ) RETURNING id',
      [user.name, user.email]
    );
    return right(`User created with ID: ${result.rows[0].id}`);
  } catch (error) {
    return left(createAppError(500, 'USER_CREATION_FAILED', 'Failed to create user'));
  }
};

export const getUser = (
  id: number
): ReaderTaskEither<Dependencies, AppError, { id: number; name: string; email: string }> => (deps) => async () => {
  try {
    const result = await deps.db.query('SELECT * FROM users WHERE id = ', [id]);
    return result.rows[0]
      ? right(result.rows[0])
      : left(createAppError(404, 'USER_NOT_FOUND', 'User not found'));
  } catch {
    return left(createAppError(500, 'USER_FETCH_FAILED', 'Failed to fetch user'));
  }
};
Nach dem Login kopieren

CRUD-Operationen (controllers/UserController.ts)

import { pipe } from 'fp-ts/function';
import { createUser, getUser } from '../services/UserService';
import { pool } from '../db';
import { AppError } from '../errors/AppError';

const errorHandler = (err: unknown, res: express.Response): void => {
  if (err instanceof AppError) {
    res.status(err.statusCode).json({ error: { code: err.code, message: err.message } });
  } else {
    res.status(500).json({ error: { code: 'UNKNOWN_ERROR', message: 'An unexpected error occurred' } });
  }
};

export const createUserHandler = (req: express.Request, res: express.Response): void => {
  pipe(
    createUser(req.body),
    (task) => task({ db: pool }),
    (promise) =>
      promise.then((result) =>
        result._tag === 'Left'
          ? errorHandler(result.left, res)
          : res.json({ message: result.right })
      )
  );
};

export const getUserHandler = (req: express.Request, res: express.Response): void => {
  pipe(
    getUser(parseInt(req.params.id, 10)),
    (task) => task({ db: pool }),
    (promise) =>
      promise.then((result) =>
        result._tag === 'Left'
          ? errorHandler(result.left, res)
          : res.json(result.right)
      )
  );
};
Nach dem Login kopieren

Express-API (index.ts)

import express from 'express';
import { createUserHandler, getUserHandler } from './controllers/UserController';

const app = express();
app.use(express.json());

// Routes
app.post('/users', createUserHandler);
app.get('/users/:id', getUserHandler);

// Start Server
app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});
Nach dem Login kopieren

Ausführen der App mit Docker und Docker Compose

Docker-Datei

# Stage 1: Build
FROM node:22 AS builder
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
RUN npm run build

# Stage 2: Run
FROM node:22
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]
Nach dem Login kopieren

docker-compose.yml

version: '3.8'
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: fp_ts_crud
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data
volumes:
  db_data:
Nach dem Login kopieren

Führen Sie die App aus – Entwicklungsmodus

# Start the database
docker-compose up -d

# Run the app
npx ts-node-dev src/index.ts
Nach dem Login kopieren

Führen Sie die App aus – Produktionsmodus

# Build the docker image
docker build -t fp-ts-crud-app .

# Start the database
docker-compose up -d

# Run the container
docker run -p 3000:3000 fp-ts-crud-app
Nach dem Login kopieren

Schreibtests

Scherz einrichten

package.json-Skripte aktualisieren:

mkdir fp-ts-crud && cd fp-ts-crud
npm init -y
npm install express pg fp-ts io-ts
npm install --save-dev typescript @types/express ts-node-dev jest @types/jest ts-jest
Nach dem Login kopieren
Nach dem Login kopieren

Beispieltest (__tests__/UserService.test.ts)

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"]
}
Nach dem Login kopieren
Nach dem Login kopieren

Abschluss

Durch die Nutzung von fp-ts, Docker und einer robusten Fehlerbehandlung haben wir eine funktionale, skalierbare und wartbare Node.js CRUD-Anwendung erstellt. Durch die Verwendung funktionaler Programmiermuster wird Ihr Code vorhersehbarer und zuverlässiger, insbesondere bei der Handhabung asynchroner Arbeitsabläufe.

Das obige ist der detaillierte Inhalt vonFunktionale Programmierung mit fp-ts in Node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage