Зачем использовать TypeScript с Node.js?

TypeScript — это язык программирования, который помогает упростить отладку и уменьшает количество ошибок во время разработки за счет применения статической типизации. Это также улучшает организацию кода и удобство сопровождения по мере усложнения приложения.

При использовании вместе с Node.js TypeScript предоставляет доступ к мощным функциям, таким как async/await, которые позволяют выполнять неблокирующий код для повышения производительности всех приложений. Вместе они предлагают идеальную комбинацию для создания масштабируемых API для обработки большого трафика и запросов.

В этой статье будут рассмотрены лучшие практики TypeScript и Node.js при совместном использовании для создания масштабируемых, удобных в сопровождении и производительных API.

Настройка проекта

Мы будем использовать платформу Express.js для нашего API, которую можно установить с помощью следующей команды:

npm install express

Далее нам нужно установить TypeScript и необходимые модули TypeScript:

npm install typescript ts-node @types/node @types/express

После установки необходимых пакетов нам нужно создать файл tsconfig.json в корневом каталоге нашего проекта. Этот файл будет содержать конфигурацию TypeScript для нашего проекта.

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "dist",
    "rootDir": "src",
    "esModuleInterop": true
  }
}

Мы поставили целью ES6, что позволяет нам использовать современные функции JavaScript. Мы также установили для модуля commonjs, модульную систему, используемую Node.js.

Далее нам нужно создать папку src для хранения файлов TypeScript и добавить файл index.ts:

import express from 'express';

const app = express();

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

Мы создали базовое приложение Express.js, которое прослушивает порт 3000 и отвечает «Hello World!» когда GET-запрос делается на корневой маршрут.

Компиляция кода

Чтобы скомпилировать код TypeScript в JavaScript, нам нужно запустить следующую команду, чтобы скомпилировать код TypeScript и сгенерировать файлы JavaScript в папке dist, указанной в файле tsconfig.json.

npx tsc

Запуск приложения

Чтобы запустить приложение, мы выполним следующую команду, чтобы запустить сервер и прослушивать порт 3000.

node dist/index.js

Масштабирование приложения

Чтобы масштабировать наше приложение, нам нужен балансировщик нагрузки для распределения входящих запросов между несколькими экземплярами нашего приложения. Одним из популярных балансировщиков нагрузки для Node.js является PM2, который можно установить с помощью следующей команды:

npm install pm2 -g

Затем мы можем запустить несколько экземпляров нашего приложения с помощью PM2:

pm2 start dist/index.js -i 2

Эта команда запустит два экземпляра нашего приложения, через которые PM2 будет автоматически распределять входящие запросы.

Примечание.Чтобы реализовать масштабируемую архитектуру, важно определить понятные интерфейсы для конечных точек API. Систему типов TypeScript можно использовать для обеспечения строгой типизации полезных данных запросов и ответов каждой конечной точки.

Рассмотрим пример API аутентификации пользователя. Мы можем определить интерфейс для полезной нагрузки запроса следующим образом:

interface LoginRequest {
  email: string;
  password: string;
}

И интерфейс полезной нагрузки ответа:

interface LoginResponse {
  token: string;
}

Затем мы можем определить конечную точку со строгой типизацией:

app.post('/login', (req: Request<{}, {}, LoginRequest>, res: Response<LoginResponse>) => {
  const { email, password } = req.body;
  // Authenticate user
  const token = generateAuthToken();
  res.json({ token });
});

В приведенном выше коде мы определяем типы Request и Response со строгой типизацией для интерфейсов LoginRequest и LoginResponse соответственно.

Еще одним важным аспектом создания масштабируемых API является корректная обработка ошибок. Блоки try-catch можно использовать для обнаружения и обработки ошибок предсказуемым образом. Мы можем определить собственные классы ошибок и вызывать их при необходимости:

class BadRequestError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'BadRequestError';
  }
}
app.post('/login', (req: Request<{}, {}, LoginRequest>, res: Response<LoginResponse>) => {
  try {
    const { email, password } = req.body;
    if (!email || !password) {
      throw new BadRequestError('Email and password are required');
    }
    // Authenticate user
    const token = generateAuthToken();
    res.json({ token });
  } catch (err) {
    if (err instanceof BadRequestError) {
      res.status(400).json({ error: err.message });
    } else {
      console.error(err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
});

Мы определили собственный класс BadRequestError и выбрасываем его, когда адрес электронной почты или пароль отсутствуют. Мы также обрабатываем ошибку, возвращая код состояния 400 с сообщением об ошибке в качестве полезной нагрузки ответа.

Чтобы обеспечить масштабируемость, нам необходимо учитывать производительность. Ключевые слова async и await можно использовать для написания асинхронного кода в синхронном стиле, улучшая читаемость и удобство сопровождения кода.

Рассмотрим сценарий запроса к базе данных. Мы можем определить асинхронную функцию для запроса базы данных и возврата обещания:

async function getUser(id: string): Promise<User> {
  // Query database
  const user = await db.query('SELECT * FROM users WHERE id = $1', [id]);
  return user;
}

Затем мы можем использовать эту функцию в нашей конечной точке следующим образом:

app.get('/users/:id', async (req: Request<{ id: string }>, res: Response<User>) => {
  const { id } = req.params;
  try {
    const user = await getUser(id);
    res.json(user);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

В приведенном выше коде мы используем ключевое слово async для определения асинхронного обработчика конечной точки. Затем мы используем ключевое слово await для вызова функции getUser и ждем ее разрешения перед возвратом ответа.

Вывод. Использование системы типов TypeScript, обработки ошибок и функций асинхронного программирования позволяет писать чистый, поддерживаемый и масштабируемый код. Node.js позволяет эффективно обрабатывать операции ввода-вывода и обеспечивает неблокирующий цикл обработки событий, который позволяет нам обрабатывать большое количество одновременных запросов. При совместном использовании мы получаем дополнительные преимущества проверки типов, интерфейсов и лучшего опыта разработчиков.

💡 Примечание. Здесь может помочь цепочка инструментов с открытым исходным кодом, такая как Bit, позволяющая вашим командам совместно использовать повторно используемые типы. strong>, чтобы уменьшить объем кода, который необходимо написать, и, таким образом, работать вместе более эффективно.

Чтобы узнать больше о совместном использовании типов между командами:



Создавайте приложения с повторно используемыми компонентами, как Lego

Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.

Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.

Подробнее

Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите:

Микро-интерфейсы

Система дизайна

Совместное использование кода и повторное использование

Монорепо

Узнать больше: