Как загружать изображения через GraphQL в React и TypeScript
При разработке на React + TypeScript часто возникает задача передачи изображений и файлов на сервер. Многие разработчики по умолчанию используют кодировку base64, передавая строку через GraphQL-мутацию. Однако этот подход неэффективен: размер данных увеличивается на 33%, а большие файлы замедляют работу приложения. Правильное решение - передача файлов в бинарном формате (Blob, File) через GraphQL, но как это сделать без REST API и с поддержкой TypeScript?
Почему base64 - не лучший выбор для загрузки файлов
Передача изображений строкой base64 через GraphQL - простой, но ресурсоёмкий метод. Во-первых, увеличивается объём передаваемых данных. Во-вторых, парсинг и декодирование на сервере требуют дополнительной памяти. Для маленьких аватарок или иконок это допустимо, но для фотографий высокого разрешения или документов лучше использовать бинарную передачу.
Apollo Upload Client и TypeScript: как решить проблему
Библиотека apollo-upload-client предоставляет функцию createUploadLink, которая заменяет стандартный HTTP-линк Apollo и позволяет отправлять файлы через FormData. Однако её типовая поддержка в официальной версии оставляет желать лучшего - типы не экспортируются корректно для TypeScript. Решение есть: установите пакет @types/apollo-upload-client или используйте apollo-upload-client версии 17+, где типы уже встроены.
Пошаговая инструкция: загрузка файла через GraphQL в React + TS
1. Установка зависимостей
Выполните команду:
npm install apollo-upload-client @types/apollo-upload-client2. Настройка Apollo Client
Замените стандартный createHttpLink на createUploadLink:
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { createUploadLink } from 'apollo-upload-client';
const client = new ApolloClient({
link: createUploadLink({ uri: 'http://localhost:4000/graphql' }),
cache: new InMemoryCache()
});3. Создание мутации на сервере
На серверной части (например, Apollo Server) определите тип Upload и мутацию:
import { GraphQLUpload } from 'graphql-upload';
const typeDefs = gql`
scalar Upload
type Mutation {
uploadFile(file: Upload!): String!
}
`;
const resolvers = {
Upload: GraphQLUpload,
Mutation: {
uploadFile: async (_, { file }) => {
const { createReadStream, filename } = await file;
// сохраните файл и верните URL
return `https://example.com/uploads/${filename}`;
}
}
};4. Отправка файла с клиента
В React-компоненте используйте мутацию с типизацией:
import { useMutation, gql } from '@apollo/client';
const UPLOAD_FILE = gql`
mutation UploadFile($file: Upload!) {
uploadFile(file: $file)
}
`;
function UploadComponent() {
const [uploadFile] = useMutation(UPLOAD_FILE);
const handleFileChange = (e: React.ChangeEvent) => {
const file = e.target.files?.[0];
if (file) {
uploadFile({ variables: { file } });
}
};
return ;
} Что делать, если apollo-upload-client не подходит
Если вы не хотите использовать стороннюю библиотеку или у вас возникают конфликты типов, есть альтернативы. Можно реализовать кастомный Apollo Link, который преобразует запрос в FormData. Или использовать GraphQL Multipart Request напрямую - спецификация поддерживается многими серверами. Ещё один вариант - передавать файл как Blob через переменную типа String, но это менее эффективно, чем нативный Upload.
Распространённые ошибки и их решение
- TypeScript ругается на типы: убедитесь, что установлен
@types/apollo-upload-clientили используйте версию 17+. - Файл не отправляется: проверьте, что сервер поддерживает скаляр
Uploadи настроен на приём multipart-запросов. - Размер файла слишком большой: настройте лимиты в Apollo Server через
maxFileSize.
Использование бинарной загрузки через GraphQL в React и TypeScript не только возможно, но и рекомендуется для производительности. Избегайте base64 для больших файлов - это упростит код и ускорит работу приложения.