Типизация обработчиков

Фреймворк Fresh использует TypeScript для обеспечения строгой типизации компонентов и обработчиков запросов, что значительно повышает безопасность кода и удобство разработки. Обработчики в Fresh представляют собой функции, которые управляют входящими HTTP-запросами и формируют ответы. Типизация этих функций позволяет явно указывать, какие методы HTTP поддерживаются, какие данные принимаются и возвращаются, а также обеспечивает корректную интеграцию с другими частями приложения.

Основные типы обработчиков

В Fresh существует несколько ключевых типов обработчиков, которые типизируются с помощью интерфейсов и дженериков:

  1. Handler Основной тип для всех обработчиков. Его сигнатура определяется так:

    import type { Handlers } from "$fresh/server.ts";
    
    export const handler: Handlers<DataType> = {
      GET(req, ctx) {
        // логика обработки GET-запроса
        return new Response(JSON.stringify(ctx.state), {
          headers: { "Content-Type": "application/json" },
        });
      },
      POST(req, ctx) {
        // логика обработки POST-запроса
      },
    };

    Здесь DataType — это тип данных, который будет доступен в ctx.state. Он позволяет TypeScript проверять структуру данных на всех этапах обработки запроса.

  2. Request и Response Встроенные объекты Request и Response полностью совместимы с Web API, однако типизация позволяет расширять их для удобства. Например, можно типизировать тело запроса:

    interface CreateUserRequest {
      name: string;
      email: string;
    }
    
    export const handler: Handlers = {
      async POST(req) {
        const data: CreateUserRequest = await req.json();
        return new Response(JSON.stringify({ success: true, user: data }));
      },
    };

    Это исключает ошибки при работе с JSON и делает код безопасным и предсказуемым.

Контекст обработчика

Объект ctx предоставляет доступ к состоянию приложения и параметрам маршрута. Его типизация позволяет гарантировать корректное использование данных:

interface State {
  user?: { id: string; name: string };
}

export const handler: Handlers<State> = {
  GET(_req, ctx) {
    if (!ctx.state.user) {
      return new Response("Unauthorized", { status: 401 });
    }
    return new Response(`Hello, ${ctx.state.user.name}`);
  },
};

Тип State определяет структуру данных, доступных внутри обработчика, что снижает риск ошибок и облегчает автодополнение в редакторе кода.

Обработчики с параметрами маршрута

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

interface Params {
  id: string;
}

export const handler: Handlers<unknown, Params> = {
  GET(_req, ctx) {
    const userId: string = ctx.params.id;
    return new Response(`User ID: ${userId}`);
  },
};

Здесь Params описывает все параметры, которые могут быть извлечены из URL. TypeScript гарантирует, что к ним нельзя обратиться иначе, чем по определённой структуре.

Асинхронные обработчики и ошибки

Обработчики в Fresh могут быть асинхронными. Типизация позволяет корректно работать с промисами и обрабатывать ошибки:

export const handler: Handlers = {
  async GET(_req) {
    try {
      const data = await fetchDataFromDB();
      return new Response(JSON.stringify(data));
    } catch (error) {
      return new Response("Internal Server Error", { status: 500 });
    }
  },
};

Использование TypeScript позволяет указывать точные типы возвращаемых данных, что помогает при интеграции с фронтендом и другими сервисами.

Типизация состояний и middleware

Fresh поддерживает middleware-подобные функции, которые модифицируют ctx.state. Типизация позволяет безопасно добавлять новые свойства:

interface AuthState {
  user: { id: string; role: string };
}

export const handler: Handlers<AuthState> = {
  GET(_req, ctx) {
    if (ctx.state.user.role !== "admin") {
      return new Response("Forbidden", { status: 403 });
    }
    return new Response("Welcome, admin");
  },
};

Такой подход предотвращает доступ к несуществующим полям и упрощает рефакторинг кода.

Итоговые принципы типизации

  • Явная типизация ctx.state и ctx.params повышает безопасность и предсказуемость кода.
  • Дженерики для Handlers позволяют точно указать типы входных и выходных данных.
  • Асинхронные функции и обработка ошибок интегрируются с TypeScript без потери строгой типизации.
  • Типизация тела запроса и ответа снижает риск ошибок при работе с JSON и внешними API.

Типизация обработчиков в Fresh превращает серверную логику в строго структурированный и легко поддерживаемый код, позволяя создавать надёжные и масштабируемые приложения на JavaScript и TypeScript.