Подключение к База данных SQL Azure и запрос с помощью пакета Node.js и mssql npm

Применимо к: База данных SQL Azure

В этом кратком руководстве описывается подключение приложения к базе данных в База данных SQL Azure и выполнение запросов с помощью Node.js и mssql. В этом кратком руководстве описан рекомендуемый подход без пароля для подключения к базе данных.

Бессерверные подключения для разработчиков

Подключения без пароля обеспечивают более безопасный механизм доступа к ресурсам Azure. Следующие высокоуровневые шаги используются для подключения к База данных SQL Azure с помощью бессерверных подключений в этой статье:

  • Подготовьте среду для проверки подлинности без пароля.
    • Для локальной среды используется ваше личное удостоверение. Это удостоверение можно извлечь из интегрированной среды разработки, интерфейса командной строки или других локальных средств разработки.
    • Для облачной среды используется управляемое удостоверение .
  • Проверка подлинности в среде с помощью DefaultAzureCredential библиотеки удостоверений Azure для получения проверенных учетных данных.
  • Используйте проверенные учетные данные для создания клиентских объектов Azure SDK для доступа к ресурсам.

Дополнительные сведения о бессерверных подключениях можно узнать в центре без пароля.

Необходимые компоненты

Настройка сервера базы данных

Безопасные, бессерверные подключения к База данных SQL Azure требуют определенных конфигураций базы данных. Проверьте следующие параметры на логическом сервере в Azure, чтобы правильно подключиться к База данных SQL Azure в локальных и размещенных средах:

  1. Для локальных подключений разработки убедитесь, что логический сервер настроен, чтобы разрешить подключение ip-адреса локального компьютера и других служб Azure:

    • Перейдите на страницу "Сеть " сервера.

    • Переключите переключатель "Выбранные сети", чтобы отобразить дополнительные параметры конфигурации.

    • Выберите " Добавить ip-адрес клиента" (xx.xx.xx.xx.xx), чтобы добавить правило брандмауэра, которое будет включать подключения с адреса IPv4 локального компьютера. Кроме того, можно выбрать + Добавить правило брандмауэра, чтобы ввести конкретный IP-адрес.

    • Убедитесь, что установлен флажок разрешить службам и ресурсам Azure доступ к этому серверу .

      Снимок экрана: настройка правил брандмауэра.

      Предупреждение

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

      Дополнительные сведения о конфигурациях безопасности базы данных см. в следующих ресурсах:

  2. На сервере также должна быть включена проверка подлинности Microsoft Entra и назначена учетная запись администратора Microsoft Entra. Для локальных подключений к разработке учетная запись администратора Microsoft Entra должна быть учетной записью, с помощью azure CLI или Visual Studio. Вы можете проверить, включена ли проверка подлинности Microsoft Entra на странице идентификатора Microsoft Entra на логическом сервере.

    Снимок экрана: включение проверки подлинности Microsoft Entra.

  3. Если вы используете личную учетную запись Azure, убедитесь, что настроена настройка Microsoft Entra и настроена для База данных SQL Azure, чтобы назначить свою учетную запись администратором сервера. Если вы используете корпоративную учетную запись, идентификатор Microsoft Entra, скорее всего, будет настроен для вас.

Создание проекта

Действия, описанные в этом разделе, создают Node.js REST API.

  1. Создайте новый каталог для проекта и перейдите в него.

  2. Инициализировать проект, выполнив следующую команду в терминале:

    npm init -y
    
  3. Установите необходимые пакеты, используемые в примере кода в этой статье:

    npm install mssql swagger-ui-express yamljs
    
  4. Установите пакет разработки, используемый в примере кода в этой статье:

    npm install --save-dev dotenv 
    
  5. Откройте проект в Visual Studio Code.

    code .
    
  6. Откройте файл package.json и добавьте следующее свойство и значение после свойства имени, чтобы настроить проект для модулей ESM.

    "type": "module",
    

Создание кода приложения Express.js

Чтобы создать приложение OpenAPI Express.js, создайте несколько файлов:

Файл Description
.env.development Файл среды только для локальной разработки.
index.js Основной файл приложения, который запускает приложение Express.js через порт 3000.
person.js Express.js файл API маршрута /person для обработки операций CRUD.
openapi.js Express.js маршрут /api-docs для пользовательского интерфейса обозревателя OpenAPI. Корневые перенаправления на этот маршрут.
openApiSchema.yml Файл схемы OpenAPI 3.0, определяющий API person.
config.js Файл конфигурации для чтения переменных среды и создания соответствующего объекта подключения mssql.
database.js Класс базы данных для обработки операций CRUD SQL Azure с помощью пакета mssql npm.
./vscode/settings.json Игнорировать файлы по шаблону glob во время развертывания.
  1. Создайте файл index.js и добавьте следующий код:

    import express from 'express';
    import { config } from './config.js';
    import Database from './database.js';
    
    // Import App routes
    import person from './person.js';
    import openapi from './openapi.js';
    
    const port = process.env.PORT || 3000;
    
    const app = express();
    
    // Development only - don't do in production
    // Run this to create the table in the database
    if (process.env.NODE_ENV === 'development') {
      const database = new Database(config);
      database
        .executeQuery(
          `CREATE TABLE Person (id int NOT NULL IDENTITY, firstName varchar(255), lastName varchar(255));`
        )
        .then(() => {
          console.log('Table created');
        })
        .catch((err) => {
          // Table may already exist
          console.error(`Error creating table: ${err}`);
        });
    }
    
    // Connect App routes
    app.use('/api-docs', openapi);
    app.use('/persons', person);
    app.use('*', (_, res) => {
      res.redirect('/api-docs');
    });
    
    // Start the server
    app.listen(port, () => {
      console.log(`Server started on port ${port}`);
    });
    
  2. Создайте файл маршрута person.js и добавьте следующий код:

    import express from 'express';
    import { config } from './config.js';
    import Database from './database.js';
    
    const router = express.Router();
    router.use(express.json());
    
    // Development only - don't do in production
    console.log(config);
    
    // Create database object
    const database = new Database(config);
    
    router.get('/', async (_, res) => {
      try {
        // Return a list of persons
        const persons = await database.readAll();
        console.log(`persons: ${JSON.stringify(persons)}`);
        res.status(200).json(persons);
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.post('/', async (req, res) => {
      try {
        // Create a person
        const person = req.body;
        console.log(`person: ${JSON.stringify(person)}`);
        const rowsAffected = await database.create(person);
        res.status(201).json({ rowsAffected });
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.get('/:id', async (req, res) => {
      try {
        // Get the person with the specified ID
        const personId = req.params.id;
        console.log(`personId: ${personId}`);
        if (personId) {
          const result = await database.read(personId);
          console.log(`persons: ${JSON.stringify(result)}`);
          res.status(200).json(result);
        } else {
          res.status(404);
        }
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.put('/:id', async (req, res) => {
      try {
        // Update the person with the specified ID
        const personId = req.params.id;
        console.log(`personId: ${personId}`);
        const person = req.body;
    
        if (personId && person) {
          delete person.id;
          console.log(`person: ${JSON.stringify(person)}`);
          const rowsAffected = await database.update(personId, person);
          res.status(200).json({ rowsAffected });
        } else {
          res.status(404);
        }
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.delete('/:id', async (req, res) => {
      try {
        // Delete the person with the specified ID
        const personId = req.params.id;
        console.log(`personId: ${personId}`);
    
        if (!personId) {
          res.status(404);
        } else {
          const rowsAffected = await database.delete(personId);
          res.status(204).json({ rowsAffected });
        }
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    export default router;
    
    
  3. Создайте файл маршрута openapi.js и добавьте следующий код для обозревателя пользовательского интерфейса OpenAPI:

    import express from 'express';
    import { join, dirname } from 'path';
    import swaggerUi from 'swagger-ui-express';
    import yaml from 'yamljs';
    import { fileURLToPath } from 'url';
    
    const __dirname = dirname(fileURLToPath(import.meta.url));
    
    const router = express.Router();
    router.use(express.json());
    
    const pathToSpec = join(__dirname, './openApiSchema.yml');
    const openApiSpec = yaml.load(pathToSpec);
    
    router.use('/', swaggerUi.serve, swaggerUi.setup(openApiSpec));
    
    export default router;
    
  4. Создайте файл схемы openApiSchema.yml и добавьте следующий YAML:

    openapi: 3.0.0
    info:
      version: 1.0.0
      title: Persons API
    paths:
      /persons:
        get:
          summary: Get all persons
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    type: array
                    items:
                      $ref: '#/components/schemas/Person'
        post:
          summary: Create a new person
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Person'
          responses:
            '201':
              description: Created
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Person'
      /persons/{id}:
        parameters:
          - name: id
            in: path
            required: true
            schema:
              type: integer
        get:
          summary: Get a person by ID
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Person'
            '404':
              description: Person not found
        put:
          summary: Update a person by ID
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Person'
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Person'
            '404':
              description: Person not found
        delete:
          summary: Delete a person by ID
          responses:
            '204':
              description: No Content
            '404':
              description: Person not found
    components:
      schemas:
        Person:
          type: object
          properties:
            id:
              type: integer
              readOnly: true
            firstName:
              type: string
            lastName:
              type: string
    

Настройка объекта подключения mssql

Пакет mssql реализует подключение к База данных SQL Azure путем предоставления параметра конфигурации для типа проверки подлинности.

  1. В Visual Studio Code создайте файл config.js и добавьте следующий код конфигурации mssql для проверки подлинности в База данных SQL Azure.

    import * as dotenv from 'dotenv';
    dotenv.config({ path: `.env.${process.env.NODE_ENV}`, debug: true });
    
    const server = process.env.AZURE_SQL_SERVER;
    const database = process.env.AZURE_SQL_DATABASE;
    const port = parseInt(process.env.AZURE_SQL_PORT);
    const type = process.env.AZURE_SQL_AUTHENTICATIONTYPE;
    
    export const config = {
        server,
        port,
        database,
        authentication: {
            type
        },
        options: {
            encrypt: true
        }
    };
    
  2. Создайте файл .env.development для переменных локальной среды и добавьте следующий текст и обновите значения <YOURSERVERNAME> для и<YOURDATABASENAME>.

    AZURE_SQL_SERVER=<YOURSERVERNAME>.database.windows.net
    AZURE_SQL_DATABASE=<YOURDATABASENAME>
    AZURE_SQL_PORT=1433
    AZURE_SQL_AUTHENTICATIONTYPE=azure-active-directory-default
    

Примечание.

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

  1. Создайте папку .vscode и создайте файл settings.json в папке.

  2. Добавьте следующее, чтобы игнорировать переменные среды и зависимости во время развертывания ZIP.

    {
        "appService.zipIgnorePattern": ["./.env*","node_modules{,/**}"]
    }
    

Добавление кода для подключения к База данных SQL Azure

  1. Создайте файл database.js и добавьте следующий код:

    import sql from 'mssql';
    
    export default class Database {
      config = {};
      poolconnection = null;
      connected = false;
    
      constructor(config) {
        this.config = config;
        console.log(`Database: config: ${JSON.stringify(config)}`);
      }
    
      async connect() {
        try {
          console.log(`Database connecting...${this.connected}`);
          if (this.connected === false) {
            this.poolconnection = await sql.connect(this.config);
            this.connected = true;
            console.log('Database connection successful');
          } else {
            console.log('Database already connected');
          }
        } catch (error) {
          console.error(`Error connecting to database: ${JSON.stringify(error)}`);
        }
      }
    
      async disconnect() {
        try {
          this.poolconnection.close();
          console.log('Database connection closed');
        } catch (error) {
          console.error(`Error closing database connection: ${error}`);
        }
      }
    
      async executeQuery(query) {
        await this.connect();
        const request = this.poolconnection.request();
        const result = await request.query(query);
    
        return result.rowsAffected[0];
      }
    
      async create(data) {
        await this.connect();
        const request = this.poolconnection.request();
    
        request.input('firstName', sql.NVarChar(255), data.firstName);
        request.input('lastName', sql.NVarChar(255), data.lastName);
    
        const result = await request.query(
          `INSERT INTO Person (firstName, lastName) VALUES (@firstName, @lastName)`
        );
    
        return result.rowsAffected[0];
      }
    
      async readAll() {
        await this.connect();
        const request = this.poolconnection.request();
        const result = await request.query(`SELECT * FROM Person`);
    
        return result.recordsets[0];
      }
    
      async read(id) {
        await this.connect();
    
        const request = this.poolconnection.request();
        const result = await request
          .input('id', sql.Int, +id)
          .query(`SELECT * FROM Person WHERE id = @id`);
    
        return result.recordset[0];
      }
    
      async update(id, data) {
        await this.connect();
    
        const request = this.poolconnection.request();
    
        request.input('id', sql.Int, +id);
        request.input('firstName', sql.NVarChar(255), data.firstName);
        request.input('lastName', sql.NVarChar(255), data.lastName);
    
        const result = await request.query(
          `UPDATE Person SET firstName=@firstName, lastName=@lastName WHERE id = @id`
        );
    
        return result.rowsAffected[0];
      }
    
      async delete(id) {
        await this.connect();
    
        const idAsNumber = Number(id);
    
        const request = this.poolconnection.request();
        const result = await request
          .input('id', sql.Int, idAsNumber)
          .query(`DELETE FROM Person WHERE id = @id`);
    
        return result.rowsAffected[0];
      }
    }
    

локальное тестирование приложения.

Приложение готово к локальному тестированию. Убедитесь, что вы вошли в облако Azure в Visual Studio Code с той же учетной записью, что и администратор базы данных.

  1. Запустите приложение с помощью следующей команды. Приложение начинается через порт 3000.

    NODE_ENV=development node index.js
    

    Таблица Person создается в базе данных при запуске этого приложения.

  2. В браузере перейдите в обозреватель OpenAPI по адресу http://localhost:3000.

  3. На странице пользовательского интерфейса Swagger разверните метод POST и выберите "Попробовать".

  4. Измените пример JSON, чтобы включить значения свойств. Свойство ID игнорируется.

    Снимок экрана, показывающий, как протестировать API.

  5. Выберите "Выполнить" , чтобы добавить новую запись в базу данных. API возвращает успешный ответ.

  6. Разверните метод GET на странице пользовательского интерфейса Swagger и выберите "Попробовать". Выберите "Выполнить", а пользователь, который вы только что создали, возвращается.

Развертывание в Службу приложений Azure

Приложение готово к развертыванию в Azure. Visual Studio Code может создать службу приложение Azure и развернуть приложение в одном рабочем процессе.

  1. Убедитесь, что приложение остановлено.

  2. Войдите в Azure, если вы еще не сделали этого, выбрав Azure: войти в облако Azure в палитре команд (CTRL SHIFT + + P)

  3. В окне Обозревателя Azure в Visual Studio Code щелкните правой кнопкой мыши узел Служба приложений s и выберите "Создать веб-приложение (дополнительно)".

  4. Чтобы создать Служба приложений, используйте следующую таблицу:

    Prompt Значение
    Введите глобально уникальное имя для нового веб-приложения. Введите запрос, например azure-sql-passwordless. После пера уникальная строка, например 123.
    Выберите группу ресурсов для новых ресурсов. Нажмите кнопку +Создать новую группу ресурсов, а затем выберите имя по умолчанию.
    Выберите стек сред выполнения Выберите версию LTS Node.js стека.
    Select an OS (Выберите ОС). Щелкните Linux.
    Выберите расположение для новых ресурсов. Выберите расположение рядом с вами.
    Выберите план Служба приложений Linux. Выберите "Создать новый Служба приложений план". Затем выберите имя по умолчанию.
    Выберите ценовую категорию. Выберите "Бесплатный" (F1).
    Select an Application Insights resource for your app (Выберите ресурс Application Insights для приложения). выберите Пока пропустить.
  5. Подождите, пока не будет создано ваше приложение, прежде чем продолжить работу.

  6. В Обозревателе Azure разверните узел Служба приложений s и щелкните правой кнопкой мыши новое приложение.

  7. Выберите "Развернуть в веб-приложении".

    Снимок экрана: Visual Studio Code в обозревателе Azure с выделенным параметром

  8. Выберите корневую папку проекта JavaScript.

  9. Когда появится всплывающее окно Visual Studio Code, нажмите кнопку "Развернуть".

Когда развертывание завершится, приложение не работает правильно в Azure. Для получения данных необходимо настроить безопасное подключение между Служба приложений и базой данных SQL.

Подключение Служба приложений к База данных SQL Azure

Для подключения экземпляра Служба приложений к База данных SQL Azure необходимо выполнить следующие действия.

  1. Создайте управляемое удостоверение для Служба приложений.
  2. Создайте пользователя базы данных SQL и свяжите его с управляемым удостоверением Служба приложений.
  3. Назначьте роли SQL пользователю базы данных, разрешающим чтение, запись и потенциально другие разрешения.

Существует несколько средств, доступных для реализации следующих действий.

Соединитель служб — это средство, которое упрощает аутентификацию подключений между различными службами в Azure. Соединитель служб в настоящее время поддерживает подключение Служба приложений к базе данных SQL Azure с помощью Azure CLI с помощью az webapp connection create sql команды. Эта одна команда выполняет три описанных выше шага.

Создание управляемого удостоверения с помощью соединителя службы

Выполните следующую команду в Cloud Shell портал Azure. Cloud Shell имеет последнюю версию Azure CLI. Замените переменные <> собственными значениями.

az webapp connection create sql \
    -g <app-service-resource-group> \
    -n <app-service-name> \
    --tg <database-server-resource-group> \
    --server <database-server-name> \
    --database <database-name> \
    --system-identity

Проверка параметров приложения Служба приложений

Изменения, внесенные соединителем службы, можно проверить в параметрах Служба приложений.

  1. В Visual Studio Code в обозревателе Azure щелкните правой кнопкой мыши Служба приложений и выберите "Открыть на портале".

  2. Перейдите на страницу удостоверений для Служба приложений. На вкладке "Назначаемая системой" состояние должно иметь значение "Вкл.". Это значение означает, что управляемое удостоверение, назначаемое системой, было включено для приложения.

  3. Перейдите на страницу конфигурации для Служба приложений. На вкладке "Параметры приложения" вы увидите несколько переменных среды, которые уже находились в объекте конфигурации mssql .

    • AZURE_SQL_SERVER
    • AZURE_SQL_DATABASE
    • AZURE_SQL_PORT
    • AZURE_SQL_AUTHENTICATIONTYPE

    Не удаляйте или не изменяйте имена или значения свойств.

Тестирование развернутого приложения

Перейдите по URL-адресу приложения, чтобы проверить, работает ли подключение к База данных SQL Azure. URL-адрес приложения можно найти на странице обзора Служба приложений.

Пользователь, созданный локально, должен отображаться в браузере. Поздравляем! Теперь приложение подключено к База данных SQL Azure как в локальных, так и в размещенных средах.

Совет

Если во время тестирования вы получаете ошибку внутреннего сервера 500, это может быть связано с конфигурациями сети базы данных. Убедитесь, что логический сервер настроен с параметрами, описанными в разделе "Настройка базы данных ".

Очистка ресурсов

После завершения работы с База данных SQL Azure удалите ресурс, чтобы избежать непредвиденных затрат.

  1. В строке поиска портал Azure найдите SQL Azure и выберите соответствующий результат.

  2. Найдите и выберите базу данных в списке баз данных.

  3. На странице обзора База данных SQL Azure нажмите кнопку "Удалить".

  4. На открывшейся странице Azure необходимо удалить... введите имя базы данных, чтобы подтвердить, а затем нажмите кнопку "Удалить".

Пример кода

Пример кода для этого приложения доступен на сайте GitHub.

Следующие шаги