Как создать безопасный Node.js GraphQL API

Опубликовано: 2022-03-11

В этой статье мы хотим представить краткое руководство по созданию безопасного Node.js GraphQL API.

Некоторые вопросы, которые могут прийти на ум, могут быть следующими:

  • Какова цель использования GraphQL API?
  • Что такое GraphQL API?
  • Что такое запрос GraphQL?
  • В чем преимущество GraphQL?
  • GraphQL лучше, чем REST?
  • Почему мы используем Node.js?

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

  • Почти каждое решение, которое вы найдете сегодня, использует какой-либо интерфейс прикладного программирования (API).
  • Даже если вы просто используете социальную сеть, такую ​​как Facebook или Instagram, вы все равно подключены к внешнему интерфейсу, использующему API.
  • Если вам интересно, вы обнаружите, что почти все развлекательные онлайн-сервисы используют другой тип API, включая такие сервисы, как Netflix, Spotify и YouTube.

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

С хорошо структурированным API можно иметь надежный, удобный в сопровождении и масштабируемый API, который может обслуживать несколько типов клиентов и интерфейсных приложений.

Тем не менее, что такое GraphQL API?

GraphQL — это язык запросов для API, разработанный для внутреннего использования в Facebook и опубликованный для общего пользования в 2015 году. Он поддерживает чтение, запись и обновления в реальном времени. Это также открытый исходный код, и его обычно сравнивают с REST и другими архитектурами. В двух словах, он основан на:

  • Запросы GraphQL — это позволяет клиенту читать и управлять тем, как данные должны быть получены.
  • Мутации GraphQL — это способ записи данных на сервер. Это соглашение GraphQL о том, как записывать данные в систему.

Хотя в этой статье предполагается продемонстрировать простой, но реальный сценарий создания и использования API-интерфейсов GraphQL, мы не будем предоставлять подробное введение в GraphQL. Причина проста, так как команда GraphQL предоставляет исчерпывающую документацию и перечисляет несколько лучших практик в своем Введении в GraphQL.

Что такое запрос GraphQL?

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

 query{ users{ firstName, lastName } }

В этом запросе мы пытаемся охватить всех пользователей из схемы наших пользователей, но получаем только firstName и lastName . Результат этого запроса будет выглядеть, например:

 { "data": { "users": [ { "firstName": "Marcos", "lastName": "Silva" }, { "firstName": "Paulo", "lastName": "Silva" } ] } }

Это довольно просто для использования клиентом.

Какова цель использования GraphQL API?

Целью создания API является возможность иметь программное обеспечение как услугу, которую можно интегрировать с другими внешними службами. Даже если ваше приложение используется одним интерфейсом, вы можете рассматривать этот интерфейс как внешнюю службу, и для этого вы сможете работать в разных проектах, когда связь между ними обеспечивается через API.

Если вы работаете в большой команде, ее можно разделить, чтобы создать команду внешнего и внутреннего интерфейса, что позволит обеим использовать одну и ту же технологию и упростить свою работу. При разработке API важно выбрать то, что лучше всего подходит для проекта и что приближает вас к желаемому решению.

В этой статье мы сосредоточимся на скелете для создания API, использующего GraphQL.

GraphQL лучше, чем REST?

Это может быть отговоркой, но я ничего не могу с собой поделать: это зависит ...

GraphQL — это подход, который очень хорошо подходит для нескольких сценариев. REST — это подход к архитектуре, который также проверен в нескольких сценариях. В настоящее время существует множество статей, объясняющих, почему один лучше другого или почему вместо GraphQL следует использовать только REST. А также множество способов, которыми вы можете использовать GraphQL внутри и по-прежнему поддерживать конечные точки API как архитектуру на основе REST.

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

Эта статья является скорее практическим руководством, чем субъективным сравнением GraphQL и REST. Если вы хотите прочитать подробное сравнение этих двух методов, я предлагаю вам ознакомиться с еще одной из наших статей, GraphQL vs. REST — учебник по GraphQL.

В сегодняшней статье мы сосредоточимся на создании API GraphQL с использованием Node.js.

Почему мы используем Node.js?

GraphQL имеет несколько разных библиотек, которые вы можете использовать. Для целей этой статьи мы решили использовать идею использования JavaScript с Node.js из-за их широкого распространения и того факта, что Node.js позволяет разработчикам использовать знакомый внешний синтаксис для разработки на стороне сервера.

Также полезно сравнить наш подход с API на основе REST, подобный тому, который был продемонстрирован в другой статье блога Toptal Engineering: Создание безопасного REST API в Node.js. В этой статье также демонстрируется использование Node.js с Express для разработки скелета REST API, который позволит вам сравнить некоторые различия между этими двумя подходами. Node.js также был разработан с масштабируемыми сетевыми приложениями, глобальным сообществом и несколькими библиотеками с открытым исходным кодом, которые вы можете найти на веб-сайте npm.

На этот раз мы собираемся показать, как создать каркас API с помощью GraphQL, Node.js и Express!

Практическое руководство по GraphQL

Как указывалось ранее, мы будем строить основу для GraphQL API, и вам нужно будет знать основы Node.js и Express, прежде чем продолжить. Исходный код проекта, созданного для этого примера GraphQL, доступен здесь.

Мы будем работать с двумя типами ресурсов:

  • Пользователи, для которых мы будем обрабатывать базовый CRUD.
  • Продукты, для которых у нас будет немного деталей, чтобы показать больше возможностей GraphQL.

Пользователи будут содержать следующую структуру:

  • я бы
  • имя
  • Фамилия
  • Эл. адрес
  • пароль
  • уровень разрешения

Продукты будут содержать следующую структуру:

  • я бы
  • имя
  • описание
  • цена

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

Давайте Код!

Прежде всего, убедитесь, что у вас установлена ​​последняя версия Node.js. На момент публикации текущая версия — 10.15.3, согласно Nodejs.org.

Инициализация проекта

Давайте начнем с новой папки, которую мы можем назвать node-graphql . Там мы можем открыть терминал или консоль Git CLI и запустить магию, используя следующую команду: npm init .

Настройка наших зависимостей и TypeScript

Чтобы ускорить процесс, замените ваш package.json на следующий в нашем репозитории Git, который должен содержать все необходимые зависимости:

 { "name": "node-graphql", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "tsc": "tsc", "start": "npm run tsc && node ./build/app.js" }, "author": "", "license": "ISC", "dependencies": { "@types/express": "^4.16.1", "@types/express-graphql": "^0.6.2", "@types/graphql": "^14.0.7", "express": "^4.16.4", "express-graphql": "^0.7.1", "graphql": "^14.1.1", "graphql-tools": "^4.0.4" }, "devDependencies": { "tslint": "^5.14.0", "typescript": "^3.3.4000" } }

С обновленным package.json просто снова нажмите на терминал и используйте: npm install . Он установит все зависимости, необходимые для запуска этого GraphQL API в Node.js и Express.

Следующая часть — настроить наш режим TypeScript. Нам нужен файл с именем tsconfig.json в нашей корневой папке со следующим:

 { "compilerOptions": { "target": "ES2016", "module": "commonjs", "outDir": "./build", "strict": true, "esModuleInterop": true } }

Логика кода для этой конфигурации будет присутствовать в папке приложения. Там мы можем создать файл app.ts и для базового тестирования добавить туда следующий код:

 console.log('Hello Graphql Node API tutorial');

Благодаря нашей конфигурации теперь мы можем запустить npm start , дождаться сборки и проверить, все ли работает правильно. В консоли терминала вы должны увидеть наш «Hello GraphQL Node API tutorial». На заднем плане конфигурация в основном компилирует код TypeScript в чистый JavaScript, а затем выполняет нашу сборку в папке build .

Теперь давайте настроим базовый скелет для нашего GraphQL API. Для запуска нашего проекта мы собираемся добавить три основных импорта:

  • выражать
  • Экспресс-график
  • Graphql-инструменты

Начнем собирать все вместе:

 import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools';

Теперь мы должны быть в состоянии начать кодировать немного. Следующим шагом будет работа с нашим приложением в Express и базовой конфигурацией GraphQL, например:

 import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools'; const app: express.Application = express(); const port = 3000; let typeDefs: any = [` type Query { hello: String } type Mutation { hello(message: String) : String } `]; let helloMessage: String = 'World!'; let resolvers = { Query: { hello: () => helloMessage }, Mutation: { hello: (_: any, helloData: any) => { helloMessage = helloData.message; return helloMessage; } } }; app.use( '/graphql', graphqlHTTP({ schema: makeExecutableSchema({typeDefs, resolvers}), graphiql: true }) ); app.listen(port, () => console.log(`Node Graphql API listening on port ${port}!`));

Что мы делаем:

  • Включение порта 3000 для нашего приложения Express server.
  • Определяем, какие запросы и мутации мы хотим использовать в качестве быстрого примера.
  • Определение того, как будут работать запросы и мутации.

Хорошо, но что происходит с typeDefs и распознавателями, а также с запросами и мутациями?

  • typeDefs — определение нашей схемы того, что мы можем ожидать от запросов и мутаций.
  • Резолверы — вместо ожидания полей или обязательных параметров здесь мы определяем функции и поведение того, как должны работать запросы и мутации.
  • Запросы — «получает», которые мы хотим прочитать с сервера.
  • Мутации — наши запросы, которые будут влиять на любые данные, которые у нас есть на нашем собственном сервере.

Теперь давайте снова запустим npm start , чтобы посмотреть, что у нас там есть. Мы ожидаем, что приложение будет работать со следующим сообщением: Node Graphql API прослушивает порт 3000!

Теперь мы можем попытаться запросить и протестировать GraphQL API на нашем собственном сервере через: http://localhost:3000/graphql.

Учебник по GraphQL: тест сервера

Отлично, теперь мы можем написать наш первый собственный запрос, который был определен как «привет».

Учебник по GraphQL: первый запрос

Обратите внимание, что то, как мы определили это в typeDefs , страница может помочь нам построить запрос.

Это здорово, но как мы можем изменить значение? Мутации!

Теперь давайте посмотрим, что произойдет, когда мы изменим наше значение в памяти с помощью мутации:

Учебник по GraphQL: демонстрация мутации

Теперь мы можем выполнять базовые операции CRUD с помощью нашего GraphQL Node.js API. Теперь давайте продвинемся с нашим кодом.

Продукты

Для продуктов мы будем использовать модуль под названием products. Чтобы упростить эту статью, мы собираемся использовать базу данных в памяти только для демонстрации. Мы определим модель и сервис для управления продуктами.

Наша модель будет основана на следующем:

 export class Product { private id: Number = 0; private name: String = ''; private description: String = ''; private price: Number = 0; constructor(productId: Number, productName: String, productDescription: String, price: Number) { this.id = productId; this.name = productName; this.description = productDescription; this.price = price; } }

Служба, которая будет взаимодействовать с GraphQL, будет определена как:

 export class ProductsService { public products: any = []; configTypeDefs() { let typeDefs = ` type Product { name: String, description: String, id: Int, price: Int } `; typeDefs += ` extend type Query { products: [Product] } `; typeDefs += ` extend type Mutation { product(name:String, id:Int, description: String, price: Int): Product! }`; return typeDefs; } configResolvers(resolvers: any) { resolvers.Query.products = () => { return this.products; }; resolvers.Mutation.product = (_: any, product: any) => { this.products.push(product); return product; }; } }

Пользователи

Для пользователей мы будем следовать той же структуре, что и модуль продуктов. У нас будет модель и сервис для пользователей. Модель будет определена как:

 export class User { private id: Number = 0; private firstName: String = ''; private lastName: String = ''; private email: String = ''; private password: String = ''; private permissionLevel: Number = 1; constructor(id: Number, firstName: String, lastName: String, email: String, password: String, permissionLevel: Number) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.email = email; this.password = password; this.permissionLevel = permissionLevel; } }

Между тем, наш сервис будет выглядеть так:

 const crypto = require('crypto'); export class UsersService { public users: any = []; configTypeDefs() { let typeDefs = ` type User { firstName: String, lastName: String, id: Int, password: String, permissionLevel: Int, email: String } `; typeDefs += ` extend type Query { users: [User] } `; typeDefs += ` extend type Mutation { user(firstName:String, lastName: String, password: String, permissionLevel: Int, email: String, id:Int): User! }`; return typeDefs; } configResolvers(resolvers: any) { resolvers.Query.users = () => { return this.users; }; resolvers.Mutation.user = (_: any, user: any) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(user.password).digest("base64"); user.password = hash; this.users.push(user); return user; }; } }

Напоминаем, что исходный код доступен для использования по этой ссылке.

Теперь мы можем поиграть и протестировать наш код. npm start . У нас будет сервер, работающий на порту 3000. Теперь мы можем получить доступ к GraphQL для тестирования по адресу http://localhost:3000/graphql.

Давайте попробуем мутацию, чтобы добавить элемент в наш список продуктов:

Демонстрация мутации Node.js GraphQL

Чтобы проверить, сработало ли это, мы теперь будем использовать запрос для продуктов, но получая только id , name и price :

 query{ products{ id, name, price } } The response will be: { "data": { "products": [ { "id": 100, "name": "My amazing product", "price": 400 } ] } }

Вот и все; продукт работает, как ожидалось. Теперь мы можем играть и переключать поля, если захотим. Вы можете попробовать добавить описание:

 query{ products{ id, name, description, price } }

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

 mutation{ user(id:200, firstName:"Marcos", lastName:"Silva", password:"amaz1ingP4ss", permissionLevel:9, email:"[email protected]") { id } }

И запрос будет таким:

 query{ users{ id, firstName, lastName, password, email } }

С ответом вроде:

 { "data": { "users": [ { "id": 200, "firstName": "Marcos", "lastName": "Silva", "password": "kpj6Mq0tGChGbZ+BT9Nw6RMCLReZEPPyBCaUS3X23lZwCCp1Ogb94/oqJlya0xOBdgEbUwqRSuZRjZGhCzLdeQ==", "email": "[email protected]" } ] } }

И вот наш скелет GraphQL готов! Отсюда предстоит сделать множество шагов к полезному, полнофункциональному API, но базовое ядро ​​уже установлено.

Резюме и заключительные мысли

Несмотря на крайнюю остроту, статья довольно большая и содержит много базовой информации о разработке GraphQL Node.js API.

Давайте рассмотрим то, что мы рассмотрели до сих пор:

  • Использование Node.js с Express и GraphQL для создания GraphQL API;
  • Базовое использование GraphQL;
  • Базовое использование запросов и мутаций;
  • Базовый подход к созданию модулей для вашего проекта;
  • Тестирование нашего GraphQL API;

Чтобы больше сосредоточиться на разработке, мы избегали нескольких важных моментов, которые можно кратко резюмировать следующим образом:

  • Валидации для новых предметов;
  • Правильная обработка ошибок с помощью общей службы ошибок;
  • Проверка полей, которые пользователь может использовать при каждом запросе общего сервиса;
  • Добавьте перехватчик JWT для защиты API;
  • Более эффективный подход к обработке хэшей паролей;
  • Добавьте модульные и интеграционные тесты;

Помните, что у нас есть полный исходный код по этой ссылке Git. Не стесняйтесь использовать, разветвлять, открывать проблемы, делать запросы на включение и играть с этим! Обратите внимание, что все стандарты и предложения, изложенные в этой статье, не высечены на камне.

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