Как создать безопасный 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.
Отлично, теперь мы можем написать наш первый собственный запрос, который был определен как «привет».
Обратите внимание, что то, как мы определили это в typeDefs
, страница может помочь нам построить запрос.
Это здорово, но как мы можем изменить значение? Мутации!
Теперь давайте посмотрим, что произойдет, когда мы изменим наше значение в памяти с помощью мутации:
Теперь мы можем выполнять базовые операции 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.
Давайте попробуем мутацию, чтобы добавить элемент в наш список продуктов:
Чтобы проверить, сработало ли это, мы теперь будем использовать запрос для продуктов, но получая только 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 еще лучше.