Conectar-se e consultar o Banco de Dados SQL do Azure usando o pacote npm Node.js e mssql

Aplica-se a:Banco de Dados SQL do Azure

Este guia de início rápido descreve como conectar um aplicativo a um banco de dados no Banco de Dados SQL do Azure e executar consultas usando Node.js e mssql. Este início rápido segue a abordagem sem senha recomendada para se conectar ao banco de dados.

Conexões sem senha para desenvolvedores

As conexões sem senha oferecem um mecanismo mais seguro para acessar os recursos do Azure. As seguintes etapas de alto nível são usadas para se conectar ao Banco de Dados SQL do Azure usando conexões sem senha neste artigo:

  • Prepare seu ambiente para autenticação sem senha.
    • Para um ambiente local: Sua identidade pessoal é usada. Essa identidade pode ser extraída de um IDE, CLI ou outras ferramentas de desenvolvimento local.
    • Para um ambiente de nuvem: uma identidade gerenciada é usada.
  • Autentique-se no ambiente usando o DefaultAzureCredential da biblioteca de Identidade do Azure para obter uma credencial verificada.
  • Use a credencial verificada para criar objetos de cliente do SDK do Azure para acesso a recursos.

Você pode saber mais sobre conexões sem senha no hub sem senha.

Pré-requisitos

Configurar o servidor de banco de dados

Conexões seguras e sem senha com o Banco de Dados SQL do Azure exigem determinadas configurações de banco de dados. Verifique as seguintes configurações em seu servidor lógico no Azure para se conectar corretamente ao Banco de Dados SQL do Azure em ambientes locais e hospedados:

  1. Para conexões de desenvolvimento local, verifique se o servidor lógico está configurado para permitir que o endereço IP da máquina local e outros serviços do Azure se conectem:

    • Navegue até a página Rede do seu servidor.

    • Alterne o botão de opção Redes selecionadas para mostrar opções de configuração adicionais.

    • Selecione Adicionar o endereço IPv4 do cliente (xx.xx.xx.xx) para adicionar uma regra de firewall que habilitará conexões do endereço IPv4 da máquina local. Como alternativa, você também pode selecionar + Adicionar uma regra de firewall para inserir um endereço IP específico de sua escolha.

    • Verifique se a caixa de seleção Permitir que os serviços e recursos do Azure acessem este servidor está marcada.

      A screenshot showing how to configure firewall rules.

      Aviso

      Habilitar a configuração Permitir que os serviços e recursos do Azure acessem esse servidor não é uma prática de segurança recomendada para cenários de produção. Aplicativos reais devem implementar abordagens mais seguras, como restrições de firewall mais fortes ou configurações de rede virtual.

      Você pode ler mais sobre configurações de segurança de banco de dados nos seguintes recursos:

  2. O servidor também deve ter a autenticação do Microsoft Entra habilitada e ter uma conta de administrador do Microsoft Entra atribuída. Para conexões de desenvolvimento local, a conta de administrador do Microsoft Entra deve ser uma conta com a qual você também pode fazer logon no Visual Studio ou na CLI do Azure localmente. Você pode verificar se o servidor tem a autenticação do Microsoft Entra habilitada na página ID do Microsoft Entra do servidor lógico.

    A screenshot showing how to enable Microsoft Entra authentication.

  3. Se estiver a utilizar uma conta pessoal do Azure, certifique-se de que tem a configuração do Microsoft Entra e configurada para a Base de Dados SQL do Azure para atribuir a sua conta como administrador de servidor. Se você estiver usando uma conta corporativa, o Microsoft Entra ID provavelmente já estará configurado para você.

Criar o projeto

As etapas nesta seção criam uma API REST Node.js.

  1. Crie um novo diretório para o projeto e navegue até ele.

  2. Inicialize o projeto executando o seguinte comando no terminal:

    npm init -y
    
  3. Instale os pacotes necessários usados no código de exemplo neste artigo:

    npm install mssql swagger-ui-express yamljs
    
  4. Instale o pacote de desenvolvimento usado no código de exemplo neste artigo:

    npm install --save-dev dotenv 
    
  5. Abra o projeto no Visual Studio Code.

    code .
    
  6. Abra o arquivo package.json e adicione a seguinte propriedade e valor após a propriedade name para configurar o projeto para módulos ESM.

    "type": "module",
    

Criar Express.js código de aplicativo

Para criar o Express.js aplicativo OpenAPI, você criará vários arquivos:

Ficheiro Description
.env.desenvolvimento Arquivo de ambiente somente de desenvolvimento local.
index.js Arquivo de aplicativo principal, que inicia o aplicativo Express.js na porta 3000.
person.js Express.js arquivo de API de rota /person para lidar com operações CRUD.
openapi.js Express.js rota /api-docs para a interface do usuário do explorador OpenAPI. Root redireciona para essa rota.
openApiSchema.yml Arquivo de esquema OpenAPI 3.0 que define a API Person.
config.js Arquivo de configuração para ler variáveis de ambiente e construir o objeto de conexão mssql apropriado.
database.js Classe de banco de dados para manipular operações CRUD do SQL do Azure usando o pacote mssql npm.
./vscode/settings.json Ignore arquivos por padrão glob durante a implantação.
  1. Crie um arquivo index.js e adicione o seguinte código:

    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. Crie um arquivo de rota person.js e adicione o seguinte código:

    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. Crie um arquivo de rota opanapi.js e adicione o seguinte código para o explorador da interface do usuário 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. Crie um arquivo de esquema openApiSchema.yml e adicione o seguinte 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
    

Configurar o objeto de conexão mssql

O pacote mssql implementa a conexão com o Banco de Dados SQL do Azure fornecendo uma definição de configuração para um tipo de autenticação.

  1. No Visual Studio Code, crie um arquivo config.js e adicione o seguinte código de configuração mssql para autenticar no Banco de Dados SQL do 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. Crie um arquivo .env.development para suas variáveis de ambiente local e adicione o texto a seguir e atualize com seus valores para <YOURSERVERNAME> e <YOURDATABASENAME>.

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

Nota

Objetos de configuração sem senha são seguros para confirmar o controle do código-fonte, pois não contêm segredos, como nomes de usuário, senhas ou chaves de acesso.

  1. Crie uma .vscode pasta e crie um arquivo settings.json na pasta.

  2. Adicione o seguinte para ignorar variáveis de ambiente e dependências durante a implantação zip.

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

Adicionar o código para se conectar ao Banco de Dados SQL do Azure

  1. Crie um arquivo database.js e adicione o seguinte código:

    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];
      }
    }
    

Testar a aplicação localmente

O aplicativo está pronto para ser testado localmente. Certifique-se de que tem sessão iniciada na Nuvem do Azure no Visual Studio Code com a mesma conta que definiu como administrador da sua base de dados.

  1. Execute o aplicativo com o seguinte comando. O aplicativo começa na porta 3000.

    NODE_ENV=development node index.js
    

    A tabela Person é criada no banco de dados quando você executa este aplicativo.

  2. Em um navegador, navegue até o explorador OpenAPI em http://localhost:3000.

  3. Na página Swagger UI, expanda o método POST e selecione Experimentar.

  4. Modifique o JSON de exemplo para incluir valores para as propriedades. A propriedade ID é ignorada.

    A screenshot showing how to test the API.

  5. Selecione Executar para adicionar um novo registro ao banco de dados. A API retorna uma resposta bem-sucedida.

  6. Expanda o método GET na página Swagger UI e selecione Experimentar. Selecione Executar e a pessoa que você acabou de criar será retornada.

Implementar no Serviço de Aplicações do Azure

O aplicativo está pronto para ser implantado no Azure. O Visual Studio Code pode criar um Serviço de Aplicativo do Azure e implantar seu aplicativo em um único fluxo de trabalho.

  1. Certifique-se de que a aplicação está parada.

  2. Entre no Azure, se ainda não o fez, selecionando o comando Azure: Entrar na Nuvem do Azure na Paleta de Comandos (Ctrl + Shift + P)

  3. Na janela Azure Explorer do Visual Studio Code, clique com o botão direito do mouse no nó Serviços de Aplicativo e selecione Criar Novo Aplicativo Web (Avançado).

  4. Use a tabela a seguir para criar o Serviço de Aplicativo:

    Prompt valor
    Insira um nome globalmente exclusivo para o novo aplicativo Web. Insira um prompt como azure-sql-passwordless. Post-pend uma cadeia de caracteres exclusiva, como 123.
    Selecione um grupo de recursos para novos recursos. Selecione +Criar um novo grupo de recursos e, em seguida, selecione o nome padrão.
    Selecione uma pilha de tempo de execução. Selecione uma versão LTS da pilha de Node.js.
    Selecione um sistema operacional. Selecione Linux.
    Selecione um local para novos recursos. Selecione um local perto de si.
    Selecione um plano do Linux App Service. Selecione Criar novo plano do Serviço de Aplicativo e, em seguida, selecione o nome padrão.
    Selecione um escalão de preço. Selecione Livre (F1).
    Selecione um recurso do Application Insights para seu aplicativo. Selecione Ignorar por enquanto.
  5. Aguarde até a notificação de que seu aplicativo foi criado antes de continuar.

  6. No Azure Explorer, expanda o nó Serviços de Aplicativo e clique com o botão direito do mouse em seu novo aplicativo.

  7. Selecione Implantar no aplicativo Web.

    Screenshot of Visual Studio Code in the Azure explorer with the Deploy to Web App highlighted.

  8. Selecione a pasta raiz do projeto JavaScript.

  9. Quando o pop-up Visual Studio Code for exibido, selecione Implantar.

Quando a implantação é concluída, o aplicativo não funciona corretamente no Azure. Você ainda precisa configurar a conexão segura entre o Serviço de Aplicativo e o banco de dados SQL para recuperar seus dados.

Conectar o Serviço de Aplicativo ao Banco de Dados SQL do Azure

As etapas a seguir são necessárias para conectar a instância do Serviço de Aplicativo ao Banco de Dados SQL do Azure:

  1. Crie uma identidade gerenciada para o Serviço de Aplicativo.
  2. Crie um usuário do banco de dados SQL e associe-o à identidade gerenciada do Serviço de Aplicativo.
  3. Atribua funções SQL ao usuário do banco de dados que permitam leitura, gravação e potencialmente outras permissões.

Há várias ferramentas disponíveis para implementar essas etapas:

O Service Connector é uma ferramenta que simplifica conexões autenticadas entre diferentes serviços no Azure. Atualmente, o Service Connector dá suporte à conexão de um Serviço de Aplicativo a um banco de dados SQL do Azure por meio da CLI do Azure usando o az webapp connection create sql comando. Este único comando completa as três etapas mencionadas acima para você.

Criar a identidade gerenciada com o Service Connector

Execute o seguinte comando no Cloud Shell do portal do Azure. O Cloud Shell tem a versão mais recente da CLI do Azure. Substitua as variáveis por <> seus próprios valores.

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

Verificar as configurações do aplicativo do Serviço de Aplicativo

Você pode verificar as alterações feitas pelo Service Connector nas configurações do Serviço de Aplicativo.

  1. No Visual Studio Code, no explorador do Azure, clique com o botão direito do rato no Serviço de Aplicação e selecione Abrir no portal.

  2. Navegue até a página Identidade do seu Serviço de Aplicativo. Na guia Sistema atribuído, o Status deve ser definido como Ativado. Esse valor significa que uma identidade gerenciada atribuída ao sistema foi habilitada para seu aplicativo.

  3. Navegue até a página Configuração do seu Serviço de Aplicativo. Na guia Configurações do aplicativo , você deve ver várias variáveis de ambiente, que já estavam no objeto de configuração mssql .

    • AZURE_SQL_SERVER
    • AZURE_SQL_DATABASE
    • AZURE_SQL_PORT
    • AZURE_SQL_AUTHENTICATIONTYPE

    Não exclua nem altere os nomes ou valores das propriedades.

Testar o aplicativo implantado

Navegue até a URL do aplicativo para testar se a conexão com o Banco de Dados SQL do Azure está funcionando. Você pode localizar a URL do seu aplicativo na página de visão geral do Serviço de Aplicativo.

A pessoa que você criou localmente deve ser exibida no navegador. Parabéns! Seu aplicativo agora está conectado ao Banco de Dados SQL do Azure em ambientes locais e hospedados.

Gorjeta

Se você receber um erro 500 Internal Server durante o teste, pode ser devido às configurações de rede do banco de dados. Verifique se o servidor lógico está configurado com as configurações descritas na seção Configurar o banco de dados .

Limpar os recursos

Quando terminar de trabalhar com o Banco de Dados SQL do Azure, exclua o recurso para evitar custos não intencionais.

  1. Na barra de pesquisa do portal do Azure, procure SQL do Azure e selecione o resultado correspondente.

  2. Localize e selecione seu banco de dados na lista de bancos de dados.

  3. Na página Visão Geral do Banco de Dados SQL do Azure, selecione Excluir.

  4. Na página Azure que você tem certeza de que deseja excluir... que é aberta, digite o nome do seu banco de dados para confirmar e selecione Excluir.

Código de exemplo

O código de exemplo para este aplicativo está disponível no GitHub.

Próximos passos