Skip to content

React-js-OTUS/otus-rest-server

 
 

Repository files navigation

Otus rest api сервер

С помощью этого сервера можно реализовать домашние работы и дипломный проект.

API

Основной url

http://19429ba06ff2.vps.myjino.ru/api

Все последующие пути добавляются к текущему

Сущности

Category

type Category = {
  id: string;
  name: string;
  photo?: string;
  createdAt: Date;
  updatedAt: Date;
  commandId: string;
};

Order

type Order = {
  id: string;
  products: OrderProduct[];
  user: User;
  status: OrderStatus;
  createdAt: Date;
  updatedAt: Date;
  commandId: string;
};

type OrderProduct = {
  _id: string; // служебный id - это не id продукта
  product: Product;
  quantity: number;
}

enum OrderStatus {
  PendingConfirmation = 'pending_confirmation',
  Processing = 'processing',
  Packaging = 'packaging',
  WaitingForDelivery = 'waiting_for_delivery',
  InTransit = 'in_transit',
  Delivered = 'delivered',
  ReturnRequested = 'return_requested',
  OrderCancelled = 'order_cancelled',
}

Operation

type Cost = {
  id: string;
  name: string;
  desc?: string;
  createdAt: Date;
  updatedAt: Date;
  amount: number;
  category: Category;
  commandId: string;
  type: 'Cost';
};

type Profit = {
  id: string;
  name: string;
  desc?: string;
  createdAt: Date;
  updatedAt: Date;
  amount: number;
  category: Category;
  commandId: string;
  type: 'Profit';
};

type Operation = Profit | Cost;

Product

type Product = {
  id: string;
  name: string;
  photo?: string;
  desc?: string;
  createdAt: Date;
  updatedAt: Date;
  oldPrice?: number;
  price: number;
  commandId: string;
  category: Category;
};

Дополнительные типы

export type Pagination = {
  pageSize?: number;
  pageNumber?: number;
};

export type Sorting = {
  type: 'ASC' | 'DESC';
  field: 'id' | 'createdAt' | 'updatedAt' | 'name';
};

Работа с токеном

Некоторые запросы открытые, другие - защищенные (PROTECTED)

Для отправки защищенных запросов нужно добавить токен в заголовок authorization и добавить префикс: Bearer ${token}

Токен можно получить при авторизации и необходимо его сохранять локально.

Авторизация

/signup POST

Регистрация нового пользователя

Параметры

type SignUpBody = {
  email: string;
  password: string;
  commandId: string;
};

При регистрации, нужно указать commandId, можно не явно. Благодаря этому вы не будете видеть данных других команд, а они не будут видеть ваши данные.

Возвращает

type AuthResult = {
  token: string;
};
/signin POST

Авторизация пользователя

Параметры

type SignUpBody = {
  email: string;
  password: string;
};

Возвращает

type AuthResult = {
  token: string;
};

Профиль

/profile GET PROTECTED

Возвращает данные профиля

Возвращает

export type Profile = {
  id: string;
  name: string;
  email: string;
  signUpDate: Date;
  commandId: string;
};
/profile POST | PUT | PATCH PROTECTED

Изменяет данные профиля. PATCH изменяет частично

Параметры

type UpdateProfileBody = {
  name: string;
};

Возвращает

export type Profile = {
  id: string;
  name: string;
  email: string;
  signUpDate: Date;
  commandId: string;
};
/profile/change-password POST PROTECTED

Изменяет пароль профиля

Параметры

type ChangePasswordBody = {
  password: string;
  newPassword: string;
};

Возвращает

type ChangePasswordResult = {
  success: boolean;
};

Категории (Общее)

/categories GET

Запрос по фильтрам. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

Параметры

type Filters = {
  name?: string;
  ids?: string[];
  pagination?: {
    pageSize?: number;
    pageNumber?: number;
  };
  createdAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  updatedAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  sorting?: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name' | 'date';
  };
};

Подготовка параметров

Используйте URLSearchParams и JSON.stringify

fetch(
  `http://url?${new URLSearchParams({
    pagination: JSON.stringify({
      pageSize: 1,
      pageNumber: 2,
    }),
    sorting: JSON.stringify({ type: 'ASC', field: 'id' }),
  }).toString()}`
)
  .then((res) => res.json())
  .then(console.log);

Возвращает

{
  data: Category[];
  pagination: {
    pageSize: number;
    pageNumber: number;
    total: number;
  };
  sorting: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name';
  }
}
/categories POST PROTECTED

Создает новую сущность

Параметры

type Params = {
  name: string;
  photo?: string;
};

Возвращает получившуюся сущность

/categories/:id GET

Возвращает по id. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

/categories/:id DELETE PROTECTED

Удаляет по id и возвращает

/categories/:id PUT PROTECTED

Обновляет по id и возвращает

Параметры

type Params = {
  name: string;
  photo?: string;
};

Возвращает получившуюся сущность

/categories/:id PATCH PROTECTED

Обновляет по id и возвращает. Пустые параметры запроса не сбрасывают существующие данные

Параметры

type Params = {
  name?: string;
  photo?: string;
};

Возвращает получившуюся сущность

Продукты (Интернет-магазин)

/products GET

Запрос фильтрам. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

Параметры

type Filters = {
  name?: string;
  ids?: string[];
  categoryIds?: string[];
  pagination?: {
    pageSize?: number;
    pageNumber?: number;
  };
  createdAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  updatedAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  sorting?: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name' | 'date';
  };
};

Подготовка параметров

Используйте URLSearchParams и JSON.stringify

fetch(
  `http://url?${new URLSearchParams({
    pagination: JSON.stringify({
      pageSize: 1,
      pageNumber: 2,
    }),
    sorting: JSON.stringify({ type: 'ASC', field: 'id' }),
  }).toString()}`
)
  .then((res) => res.json())
  .then(console.log);

Возвращает

{
  data: Product[];
  pagination: {
    pageSize: number;
    pageNumber: number;
    total: number;
  };
  sorting: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name';
  }
}
/products POST PROTECTED

Создает новую сущность

Параметры

type Params = {
  name: string;
  photo?: string;
  desc?: string;
  oldPrice?: number;
  price: number;
  categoryId: string;
};

Возвращает получившуюся сущность

/products/:id GET

Возвращает по id. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

/products/:id DELETE PROTECTED

Удаляет по id и возвращает

/products/:id PUT PROTECTED

Обновляет по id и возвращает

Параметры

type Params = {
  name: string;
  photo?: string;
  desc?: string;
  oldPrice?: number;
  price: number;
  categoryId: string;
};

Возвращает получившуюся сущность

/products/:id PATCH PROTECTED

Обновляет по id и возвращает. Пустые параметры запроса не сбрасывают существующие данные

Параметры

type Params = {
  name?: string;
  photo?: string;
  desc?: string;
  oldPrice?: number;
  price?: number;
  categoryId?: string;
};

Возвращает получившуюся сущность

Заказы (Интернет-магазин)

/orders GET

Запрос фильтрам. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников.

Параметры

type Filters = {
  productIds?: string[];
  userId?: string;
  ids?: string[];
  status?: OrderStatus;
  pagination?: {
    pageSize?: number;
    pageNumber?: number;
  };
  createdAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  updatedAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  sorting?: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name' | 'date';
  };
};

Подготовка параметров

Используйте URLSearchParams и JSON.stringify

fetch(
  `http://url?${new URLSearchParams({
    pagination: JSON.stringify({
      pageSize: 1,
      pageNumber: 2,
    }),
    sorting: JSON.stringify({ type: 'ASC', field: 'id' }),
  }).toString()}`
)
  .then((res) => res.json())
  .then(console.log);

Возвращает

{
  data: Order[];
  pagination: {
    pageSize: number;
    pageNumber: number;
    total: number;
  };
  sorting: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name';
  }
}
/orders POST PROTECTED

Создает новую сущность

Параметры

type Params = {
  products: Array<{
    id: string;
    quantity: number;
  }>;
  status?: OrderStatus;
};

Возвращает получившуюся сущность

/orders/:id GET

Возвращает по id. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

/orders/:id DELETE PROTECTED

Удаляет по id и возвращает

/orders/:id PUT PROTECTED

Обновляет по id и возвращает

Параметры

type Params = {
  productIds: string[];
  status: OrderStatus;
};

Возвращает получившуюся сущность

/orders/:id PATCH PROTECTED

Обновляет по id и возвращает. Пустые параметры запроса не сбрасывают существующие данные

Параметры

type Params = {
  productIds?: string[];
  status?: OrderStatus;
};

Возвращает получившуюся сущность

Операции (Учет доходов/расходов)

/operations GET

Запрос фильтрам. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

Параметры

type Filters = {
  ids?: string[];
  name?: string;
  categoryIds?: string[];
  type?: 'Cost' | 'Profit';
  pagination?: {
    pageSize?: number;
    pageNumber?: number;
  };
  date?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  createdAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  updatedAt?: {
    gte?: string; // от - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
    lte?: string; // до - дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  }
  sorting?: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name' | 'date';
  };
};

Подготовка параметров

Используйте URLSearchParams и JSON.stringify

fetch(
  `http://url?${new URLSearchParams({
    pagination: JSON.stringify({
      pageSize: 1,
      pageNumber: 2,
    }),
    sorting: JSON.stringify({ type: 'ASC', field: 'id' }),
  }).toString()}`
)
  .then((res) => res.json())
  .then(console.log);

Возвращает

{
  data: Operation[];
  pagination: {
    pageSize: number;
    pageNumber: number;
    total: number;
  };
  sorting: {
    type: 'ASC' | 'DESC';
    field: 'id' | 'createdAt' | 'updatedAt' | 'name';
  }
}
/operations POST PROTECTED

Создает новую сущность

Параметры

type Params = {
  name: string;
  desc?: string;
  amount: number;
  date: string; // дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  type: 'Profit' | 'Cost';
  categoryId: string;
};

Возвращает получившуюся сущность

/operations/:id GET

Возвращает по id. Авторизация не обязательна, но с ней вы будете видеть только свои данные, а не всех учеников

/operations/:id DELETE PROTECTED

Удаляет по id и возвращает

/operations/:id PUT PROTECTED

Обновляет по id и возвращает

Параметры

type Params = {
  name: string;
  desc?: string;
  amount: number;
  date: string; // дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  type: 'Profit' | 'Cost';
  categoryId: string;
};

Возвращает получившуюся сущность

/operations/:id PATCH PROTECTED

Обновляет по id и возвращает. Пустые параметры запроса не сбрасывают существующие данные

Параметры

type Params = {
  name?: string;
  desc?: string;
  amount?: number;
  date?: string; // дата в виде строки new Date().toISOString() 2023-09-19T10:37:16.389+00:00
  type?: 'Profit' | 'Cost';
  categoryId?: string;
};

Возвращает получившуюся сущность

Файлы (Общее)

/upload POST PROTECTED

Загружает файлы на сервер.

Возвращает

{
  "url": "..."
}
Пример загрузки изображения на сервер с помощью fetch
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const [file] = e.target.files;
  const body = new FormData();
  // важно использовать название file append('file', ...) иначе работать не будет
  body.append('file', file);
  fetch('http://19429ba06ff2.vps.myjino.ru/api/upload', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
    method: 'POST',
    body,
  })
    .then(res => res.json())
    .then(({ url }) => onChange(url))
    .catch((err) => {
      console.error(err);
    })
};

...

<input type="file" onChange={handleChange} />
Пример загрузки изображения на сервер с помощью XMLHttpRequest
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const [file] = e.target.files;
  const body = new FormData();
  // важно использовать название file append('file', ...) иначе работать не будет
  body.append('file', file);
  new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = function (event) {
      // Здесь можно получать количество отправленных данных на сервер, что позволяет сделать индикатор загрузки изображения  
      onProgress(event.loaded, event.total);
    };
    xhr.onload = function () {
      if (xhr.status !== 200) {
        reject(xhr);
      } else {
        resolve(JSON.parse(xhr.response));
      }
    };

    xhr.onerror = () => {
      Object.assign(xhr, { message: 'unknown error' });
      reject(xhr);
    };

    xhr.open('POST', 'http://19429ba06ff2.vps.myjino.ru/api/upload');

    xhr.setRequestHeader('Authorization', `Bearer ${token}`);

    xhr.send(body);
  })
    .then(({ url }) => onChange(url))
    .catch((err) => {
      console.error(err);
    })
};

...

<input type="file" onChange={handleChange} />

Ошибки

Все ошибки с сервера приходят в формате

type ServerErrors = {
  errors: {
    extensions: {
      code: ErrorCode;
    };

    name: string;
    fieldName?: string;
    stack: string;
    message: string;
  }[];
}

enum ErrorCode {
  ERR_INCORRECT_EMAIL_OR_PASSWORD = 'ERR_INCORRECT_EMAIL_OR_PASSWORD', // Если не корректный email или пароль
  ERR_ACCOUNT_ALREADY_EXIST = 'ERR_ACCOUNT_ALREADY_EXIST', // При регистрации если пользователь уже существует
  ERR_FIELD_REQUIRED = 'ERR_FIELD_REQUIRED', // Обязательное поле. В ошибке будет дополнительное поле fieldName с указанием, какое конкретно поле обязательно
  ERR_INCORRECT_PASSWORD = 'ERR_INCORRECT_PASSWORD', // Некорректный старый пароль при попытке его изменить
  ERR_INVALID_PASSWORD = 'ERR_INVALID_PASSWORD', // Пароль не соответствует регулярному выражению /^[\w-@{}()#$%^&*+=!~]{8,}$/
  ERR_NOT_VALID = 'ERR_NOT_VALID', // Не валидный id сущности
  ERR_AUTH = 'ERR_AUTH', // Токен не передан, либо не прошел авторизацию
  ERR_NO_FILES = 'ERR_NO_FILES', // Ошибка при загрузке файлов
  ERR_NOT_ALLOWED = 'ERR_NOT_ALLOWED', // Нет доступа к данной операции (нельзя редактировать заказ другого пользователя)
  ERR_NOT_FOUND = 'ERR_NOT_FOUND', // Сущность не найдена
  ERR_VALIDATION_ERROR = 'ERR_VALIDATION_ERROR', // Не валидные данные, например, не указано name
  ERR_INVALID_QUERY_PARAMS = 'ERR_INVALID_QUERY_PARAMS', // Все GET запросы могут принимать данные запроса в search params в формате { [key: string]: string // Нужно использовать JSON.stringify() }
  
  ERR_INTERNAL_SERVER = 'ERR_INTERNAL_SERVER', // Серверная ошибка. Обратитесь ко мне, этой ошибки быть не должно
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 99.4%
  • JavaScript 0.6%