Русский
Русский
English
Статистика
Реклама

Recovery mode TypedAPI клиент-сервер для TypeScript



Предлагаю вашему вниманию TypedAPI: библиотеку API клиента-сервера для проектов, в которых фронт и бэк написаны на TypeScript. Акцент делается на максимальной простоте использования. Суть такая: вы пишете API как обычный TypeScript класс с некоторыми ограничениями, потом автоматом из класса строится интерфейс, которым будет пользоваться клиентское приложение. Также генерируется некоторая служебная информация. Остается только настроить коннекторы (доступны HTTP и WebSocket). Валидацию, хранение подключений, и др. берет на себя TypedAPI. Есть поддержка событий.

Чтобы поиграться, есть два простеньких демонстрационных приложения: Hello, world! и Демо-чат.

Установка


TypedAPI состоит из нескольких библиотек, которые подключаются в зависимости от типа использования.



Пример, если мы хотим использовать WebSocket соединение:
# на сервереnpm install --save typedapi-core typedapi-server typedapi-server-wsnpm install --save-dev typedapi-parser# на клиентеnpm install --save typedapi-core typedapi-client typedapi-client-browser-ws

Для HTTP соединения:
# for servernpm install --save typedapi-core typedapi-servernpm install --save-dev typedapi-parser# for clientnpm install --save typedapi-core typedapi-client typedapi-client-browser-http


Требования к API классу


  • Все методы должны возвращать Promise
  • Есть ограничения по типу данных, которые могут принимать и возвращать методы (см. ниже)
  • Класс может содержать дочерние свойства-объекты, построеные по тем же правилам

Дополнительно класс может содержать свойства типа Event и ParametricEvent для реализации событий, и есть заранее определенные инъекции для авторизации и хранения базовой информации о пользователе (см. ниже).
Пример класса для Hello, world!:

export class Api {    async hello(name: string): Promise<string> {        return `Hello, ${name}!`    }}

Ограничения по типам данных


Методы могут возвращать/принимать следующие типы данных:

  • Скалярные: number, string, boolean, Date, undefined, null
  • Array, Tuple, Enum, Union. Могут содержать только описанные здесь типы.
  • Объекты-структуры без методов
  • Индексированные объекты типа { [key: string | number]: SomeOtherType }

Прием/передача any, unknown запрещена.

Генерация интерфейса


Генерация интерфейса производится путем использования команды typedapi-parse из пакета typedapi-parser:

typedapi-parse [sourceFilename] [sourceObjectName] [outFilename] [reflectionOutFileName]

где:

  • sourceFilename: Путь к файлу, где хранится ваш API
  • sourceObjectName: Название класса в этом файле
  • outFilename: Путь к файлу, в который будет записан интерфейс для клиента
  • reflectionOutFileName: Путь к файлу, который будет хранить reflections для всех методов и данных. Он используется на сервере для валидации данных


Создание соединения


После того, как вы настроили API и сегенерировали интерфейсы, можно настраивать соединение. Пример как это может выглядеть для WebSocket:

Сервер
import { WebSocketServer } from "typedapi-server-ws"import { buildMap } from "typedapi-server"// файл, сгенерированный командой typedapi-parse для сервераimport { reflection } from "./apiReflection"// Ваш API классimport { Api } from "./Api"new WebSocketServer({    apiMap: buildMap(reflection, new Api),    port: 8090})

Клиент
import { WebSocketTransport } from "typedapi-client-browser-ws"// файл, сгенерированный командой typedapi-parse для клиентаimport { createClient } from "./apiReflection"const api = createClient({ transport })// теперь можно вызывать методы APIlet result = await api.hello(name)


Настройка HTTP соединения происходит аналогичным способом, можно посмотреть в документации.

События


TypedAPI поддерживает события. При WebSocket соединении оповещение происходит обычным образом, отправкой данных через сокет. При HTTP соединении используется HTTP polling.

Пример cоздания события на сервере:
import { Event } from "typedapi-server"export class Api {    someEvent = new Event<string>()}// отправляем событие всем продписчикамapi.someEvent.fire(data)// отправляем событие одному пользователюapi.someEvent.fireForUser(data, userId)// Отправляем группе пользователейapi.someEvent.fireForGroup(data, groupName)// отправляем конкретной сессииapi.someEvent.fireForSession(data, sessionId)// Отправялем конкретному соединениюapi.someEvent.fireForConnection(data, connectionId)

Обработка на клиенте:
// Подписываемся на событиеconst subscription = await api.someEvent.subscribe(data => {    // обработка события})// Отпискаawait subscription.unsubscribe()


Параметрические события


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

Авторизация


Для того, чтобы реализовать авторизацию в вашем API, надо:
  • Реализовать SessionProviderInterface и передать его в конструктор сервиса. По умолчанию используется MemorySessionProvider, который будет сбрасываться при каждой перезагрузке сервера.
  • Реализовать методы, которые возвращают AuthDataResponse. Это специальный ответ метода, который обрабатывается по другому, нежели другие ответы.

Интерфейс AuthDataResponse выглядит так:
type AuthDataResponse = {    newAuthData: {        id?: string | number        groups?: string[]        name?: string        email?: string        phone?: string    }    // ответ, который будет отправлен пользователю    // newAuthData используется только для внутренних нужд    response: boolean}

пример реализации API с авторизацией:
import { AuthDataResponse } from "typedapi-server"export class ClientApi {    /**     * Проверяем логин и пароль, если подходит авторизуем    **/    async login(username: string, password: string): Promise<AuthDataResponse> {        let user = await usersRepository.login(username, password)        if(!user) {            return {                response: false,                newAuthData: {}            }        } else {            return {                response: true,                newAuthData: {                    id: user.id,                    groups: user.groups                }            }        }    }    /**    * Logout    **/    async logout(): Promise<AuthDataResponse> {        return {            response: true,            newAuthData: {}        }    }    /**    * в apiUserID будет автоматически добавлен идентификатор пользователя.    * Если пользватель не авторизован, он получит NotAuthorizedError    **/    async getUserData(apiUserId: number): Promise<SomeUserData> {        let userData = await usersRespotory.getUserData(apiUserId)        return userData    }}


Также помимо apiUserId можно получать и другие данные о пользователе, подробнее в документации.

Микросервисы


Для разбиения API на микросервисы используется следующая идея: на каждом микросервисе поднимается экземпляр API, и с помощью для каждого экземпляра устанавливается, какой сервис будет отвественен за обработку определенных методов. Для этого есть следующие инструменты:
  • HttpProxyClient
    Это объект проксирует вызовы методов со входного API на внутренний сервис
  • HttpTrustServer
    Этот объект принимает вызов от HttpProxyClient и вызывает запрашиваемый метод
  • RedisPublisher
    Присоединяется к API, слушает события, и перенаправляет их в Redis. Используется во внутренних сервисах.
  • RedisSubscriber
    Слушает события из Редиса, и запускает их на входном API

Архитектура может выглядеть примерно так:

Примеры кода можно найти в документации.

Заключение


Я описал ключевые особенности TypedAPI, для дополнительной информации можно почитать документацию. Я уже использую эту библиотеку в паре своих проектов, но пока она все же находится в режиме беты. Я активно ее развиваю, и надеюсь, что она будет полезна людям. Буду рад любой конструктивной критике.

Спасибо за внимание.
Источник: habr.com
К списку статей
Опубликовано: 21.04.2021 20:21:36
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Javascript

Api

Typescript

Client-server

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru