Node.js + Angular: лучшие практики для бесшовной интеграции API GraphQL

Node.js + Angular: лучшие практики для бесшовной интеграции API GraphQL

20 июня 2025 г.

Современные приложения для полного стека часто используют фронта JavaScript, такие как Angular, и бэкэнд, построенный с Node.js. Эти слои общаются через API, и GraphQL является популярным выбором для того, чтобы сделать эти API более гибкими.


С GraphQL угловой клиент запрашивает именно те данные, которые ему нужны, в то время как сервер Node.js извлекает эти данные из баз данных или других сервисов. Поскольку схема GraphQL разделяется между фронталом и бэкэнд, фронт может развиваться независимо. Клиент отправляет на сервер запросы graphQL или мутации, и сервер обрабатывает их с помощью функций резолей. Этот подход избегает создания многочисленных фиксированных конечных точек REST и позволяет Frontend извлекать только конкретные поля, которые в нем нуждаются.


Аутентификация

Общим методом для обеспечения запросов является использование JWT (JSON Web Token). После входа пользователя бэкэнд выпускает JWT. Angular App должно сохранить этот токен и включать его с каждым запросом GraphQL. Например, это можно сделать с помощью Apollo Angular'ssetContextпромежуточное программное обеспечение:

const auth = setContext((_, { headers }) => {

  const token = localStorage.getItem('token');

  return token ? { headers: { Authorization: `Bearer ${token}` } } : {};

});

const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' });

apollo.create({ link: auth.concat(httpLink), cache: new InMemoryCache() });


Этот код прикрепляет авторизацию: носитель <token> к каждому запросу, когда пользователь входит в систему. На стороне Node.js функция контекста Apollo Server может читать и проверять этот токен по каждому запросу. Например:

const server = new ApolloServer({

  typeDefs,

  resolvers,

  context: ({ req }) => {

    const token = req.headers.authorization || '';

    const user = verifyJwt(token);  // your function to verify the JWT

    return { user };
  }
});


Затем резолюры могут проверить Context.user. Если Context.user отсутствует или недействителен, вы должны отклонить запрос. Лучшая практика - бросить GraphQlerror с четким кодом. Например:

if (!context.user) {
  throw new GraphQLError('Not authenticated', {
    extensions: { code: 'UNAUTHENTICATED' }
  });
}


Использование конкретных кодов, таких как несаутентированные или запрещенные, помогает угловому клиенту. Вы также можете проверить Context.user на роли или разрешения и бросить GraphQlerror с кодом.


CORS (перекрестный обмен ресурсами)

Если Angular App и Node Server работают на разных доменах или портах, браузеры блокируют запросы по умолчанию. Чтобы разрешить запросы, включите COR на сервере. В экспресс -настройке вы можете использовать промежуточное программное обеспечение CORS:

const cors = require('cors');

app.use(cors());  // Allow all origins (for development only)


Эта строка позволяет любому происхождению доступ к API. В производстве вы должны ограничить это своим фактическим доменом фронт-энда. Например:

app.use(

  '/graphql',

  cors({ origin: 'http://localhost:4200', credentials: true }),

  express.json(),

  expressMiddleware(server)

);


Эта конфигурация только позволяет запросам отhttp: // localhost: 4200Достигните конечную точку /graphql. Учетные данные: True Option означает, что файлы cookie или автозаголов разрешены. С точки зрения угловой стороны, если бы вы использовали файлы cookie для аутентификации, вы бы установили с Credentials: True в ссылке HTTP. Однако, поскольку мы посылаем JWT в заголовках, как показано выше, вам обычно не нужны файлы cookie или credentials.


Обработка ошибок в GraphQL

GraphQL отличается от отдыха: он всегда возвращает200 ОКСтатус http, даже если есть ошибки. Ошибки появляются в полезной нагрузке. Лучшая практика - использовать структурированные объекты ошибки с кодами. Apollo Server автоматически присваивает коды, но вы также можете бросить пользовательские ошибки.


Например:

const { GraphQLError } = require('graphql');

if (args.value < 0) {

  throw new GraphQLError('Value must be non-negative', {

    extensions: { code: 'BAD_USER_INPUT', argumentName: 'value' }

  });

}


Это даст ответ с ошибкой, такой как:

"errors": [

  {

    "message": "Value must be non-negative",

    "extensions": {

      "code": "BAD_USER_INPUT",

      "argumentName": "value"

    }

  }

]


Включение расширений. Код помогает угловому клиенту точно знать, что пошло не так, например, показ проверки сообщения. На стороне клиента вы обрабатываете ошибки GraphQL в своих вызовах Apollo.


Например:

this.apollo.watchQuery({ query: GET_DATA }).valueChanges.subscribe({

  next: ({ data, errors }) => {

    // data contains the result; errors contains any GraphQL errors

  },

  error: (networkError) => {

    console.error('Network error:', networkError);

  }

});

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


Оптимизация производительности

Одной из общих неэффективности в GraphQL является проблема «n+1 запросов», когда резольвер может сделать вызов базы данных для каждого элемента в списке. FacebookDataLoaderБиблиотека является популярным решением для партии и кэширования этих вызовов.


Например:

const DataLoader = require('dataloader');

const userLoader = new DataLoader(async (ids) => {

  return await UserModel.find({ id: { $in: ids } });

});

 

const server = new ApolloServer({

  typeDefs,

  resolvers,

  context: () => ({ loaders: { user: userLoader } })

});


В ваших резолюрах используйте context.loaders.user.load (userId) вместо того, чтобы напрямую запросить базу данных. DataLoader будет разыграть несколько вызовов (...) для разных идентификаторов в один запрос находки. Это сокращает избыточные запросы базы данных и повышает производительность.


Другие советы по производительности включают:

  • Кэширование ответа:Apollo Server может кэш -запрос приводит к памяти или внешнему хранилищу, чтобы ускорить повторные запросы.
  • Автоматические сохраняемые запросы:Эта функция отправляет только идентификатор запроса вместо полного текста GraphQL, уменьшая полезную нагрузку сети.
  • Ограничения сложности запроса:Используйте библиотеки, такие как график-глубины или график, чтобы предотвратить очень глубокие или дорогие запросы.
  • Кэширование на стороне клиента:Кэш в памяти Apollo в угловой кости сохраняет извлеченные данные, поэтому компоненты могут повторно использовать его без переиздания.

Комбинирование партии (с DataLoader), кэширование и ограничение запросов делают ваше приложение быстрее и масштабируемым.


Заключение

Все вместе, Node.js, Angular и GraphQL создают чистое, но мощное решение для полного стека. Server Node.js GraphQL - это всего лишь одна конечная точка, которая скрывает сложность бэкэнд. Сервер может ограничить доступ с помощью аутентификации, политик CORS и обработки структурированных ошибок. Угловой клиент с клиентом Apollo делает запросы и обновления простыми, используя преимущества кэширования и обновления в реальном времени из коробки. Разработчики фронта пишут богатые запросы и мутации, не беспокоясь о конечных точках отдыха или чрезмерной поглощении.


Ключевые лучшие практики:

  • Jwt аутентификация: Поместите JWT в заголовок авторизации по всем запросам.
  • Включить CORS с умом: Позвольте только исходным приложениям вашего углового приложения получить доступ к API.
  • Структурированные ошибки:Бросьте GraphQlerror с четкими расширениями, значениями кода.
  • Оптимизированная загрузка данных:Используйте DataLoader и Apollo Caching для пакетных запросов и данных кэша.


Следуя этой практике, ваш код будет более безопасным, оптимизированным и обслуживаемым.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE