Conectar-se ao Banco de Dados SQL do Azure e consultá-lo usando o pacote npm Node.js e mssql

Aplica-se a: Banco de Dados SQL do Azure

Este 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 o Node.js e mssql. Este guia de início rápido segue a abordagem sem senha recomendada para se conectar ao banco de dados.

Conexões sem senha para desenvolvedores

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 conectar-se 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 uma IDE, CLI ou outras ferramentas de desenvolvimento local.
    • Para um ambiente em nuvem: é usada uma identidade gerenciada.
  • 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 Azure SDK 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 para o Banco de Dados SQL do Azure exigem determinadas configurações do banco de dados. Verifique as seguintes configurações no seu servidor lógico do 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 do computador local e outros serviços do Azure se conectem:

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

    • Clique no 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á as conexões do endereço IPv4 do computador 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 serviços e recursos do Azure acessem este servidor está marcada.

      Uma captura de tela mostrando como configurar regras de firewall.

      Aviso

      Habilitar a configuração Permitir que serviços e recursos do Azure acessem esse servidor não é uma prática de segurança recomendada para cenários de produção. Os 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 do administrador do Microsoft Entra atribuída. Para conexões de desenvolvimento local, a conta do 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 Microsoft Entra ID do servidor lógico.

    Uma captura de tela mostrando como habilitar a autenticação do Microsoft Entra.

  3. Se você estiver usando uma conta pessoal do Azure, verifique se tem a instalação e configuração do Microsoft Entra para o Banco de Dados SQL do Azure para atribuir sua conta à administração do servidor. Se você estiver usando uma conta corporativa, o Microsoft Entra ID provavelmente já estará configurado.

Criar o projeto

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

  1. Crie um 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 express swagger-ui-express yamljs dotenv
    
  4. Abra o projeto no Visual Studio Code.

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

    "type": "module",
    

Criar o código de aplicativo Express.js

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

Arquivo Descrição
.env.development Arquivo de ambiente somente desenvolvimento local.
index.js Arquivo de aplicativo principal, que inicia o aplicativo Express.js na porta 3000.
person.js Arquivo de API de rota /person do Express.js para manipular operações CRUD.
openapi.js Rota /api-docs do Express.js para interface do usuário do Gerenciador OpenAPI. A raiz redireciona para essa rota.
openApiSchema.yml Arquivo de esquema OpenAPI 3.0 definindo a API de Pessoa.
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 npm mssql.
./vscode/settings.json Ignorar 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 App routes
    import person from './person.js';
    import openapi from './openapi.js';
    
    const port = process.env.PORT || 3000;
    
    const app = express();
    
    // 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 { 
      passwordConfig as SQLAuthentication, 
      noPasswordConfig as PasswordlessConfig 
    } from './config.js';
    import { createDatabaseConnection } from './database.js';
    
    const router = express.Router();
    router.use(express.json());
    
    const database = await createDatabaseConnection(SQLAuthentication);
    
    router.get('/', async (req, 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 {
        // add 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;
    

    Para autenticação sem senha, altere o parâmetro passado em createDatabaseConnection de SQLAuthentication para PasswordlessConfig.

    const database = await createDatabaseConnection(PasswordlessConfig);
    
  3. Crie um arquivo de rota opanapi.js e adicione o seguinte código para o gerenciador de interface do usuário do 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;
    

Configurar o objeto de conexão mssql

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

  1. No Visual Studio Code, crie um arquivo config.js e adicione o código de configuração mssql a seguir para autenticar-se no Banco de Dados SQL do Azure.

    import * as dotenv from 'dotenv';
    
    if(process.env.NODE_ENV === 'development') {
      dotenv.config({ path: `.env.${process.env.NODE_ENV}`, debug: true });
    }
    
    // TIP: Port must be a number, not a string!
    const server = process.env.AZURE_SQL_SERVER;
    const database = process.env.AZURE_SQL_DATABASE;
    const port = +process.env.AZURE_SQL_PORT;
    const type = process.env.AZURE_SQL_AUTHENTICATIONTYPE;
    const user = process.env.AZURE_SQL_USER;
    const password = process.env.AZURE_SQL_PASSWORD;
    
    export const noPasswordConfig = {
      server,
      port,
      database,
      authentication: {
        type
      },
      options: {
        encrypt: true
      }
    };
    
    export const passwordConfig = {
      server,
      port,
      database,
      user,
      password,
      options: {
        encrypt: true
      }
    };
    

Crie um arquivo de variável de ambiente local

Crie um arquivo .env.development para suas variáveis de ambiente locais

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

Observação

Os 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.

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

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

    import sql from 'mssql';
    
    let database = null;
    
    export default class Database {
      config = {};
      poolconnection = null;
      connected = false;
    
      constructor(config) {
        this.config = config;
      }
    
      async connect() {
        try {
          this.poolconnection = await sql.connect(this.config);
          this.connected = true;
          console.log('Database connected successfully.');
          return this.poolconnection;
        } catch (error) {
          console.error('Error connecting to the database:', error);
          this.connected = false;
        }
      }
    
      async disconnect() {
        try {
          if (this.connected) {
            await this.poolconnection.close();
            this.connected = false;
            console.log('Database disconnected successfully.');
          }
        } catch (error) {
          console.error('Error disconnecting from the database:', error);
        }
      }
    
      async executeQuery(query) {
        const request = this.poolconnection.request();
        const result = await request.query(query);
    
        return result.rowsAffected[0];
      }
    
      async create(data) {
        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() {
        const request = this.poolconnection.request();
        const result = await request.query(`SELECT * FROM Person`);
    
        return result.recordsets[0];
      }
    
      async read(id) {
        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) {
        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) {
        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];
      }
    
      async createTable() {
        if (process.env.NODE_ENV === 'development') {
          this.executeQuery(
            `IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Person')
             BEGIN
               CREATE TABLE Person (
                 id int NOT NULL IDENTITY, 
                 firstName varchar(255), 
                 lastName varchar(255)
               );
             END`
          )
            .then(() => {
              console.log('Table created');
            })
            .catch((err) => {
              // Table may already exist
              console.error(`Error creating table: ${err}`);
            });
        }
      }
    }
    
    export const createDatabaseConnection = async (passwordConfig) => {
      database = new Database(passwordConfig);
      await database.connect();
      await database.createTable();
      return database;
    };
    

Testar o aplicativo localmente

O aplicativo está pronto para ser testado localmente. Verifique se você está conectado à Nuvem do Azure no Visual Studio Code com a mesma conta definida como o administrador do banco de dados.

  1. Execute o aplicativo com o comando a seguir. O aplicativo é iniciado na porta 3000.

    NODE_ENV=development node index.js
    

    A tabela Pessoa é criada no banco de dados quando você executa esse aplicativo.

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

  3. Na página interface do usuário do Swagger, expanda o método POST e selecione Experimentar.

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

    Uma captura de tela mostrando como testar a 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 da interface do usuário do Swagger e selecione Experimentar. Selecione Executar e a pessoa que você acabou de criar será retornada.

Configurar projeto para implantação zip

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

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

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

Implantar no Serviço de Aplicativo 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. Verifique se o aplicativo foi interrompido.

  2. Entre no Azure, caso ainda não tenha feito isso, 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 Aplicativos e selecione Criar Aplicativo Web (Avançado).

  4. Use a seguinte tabela 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. Acrescente uma cadeia de caracteres exclusiva, como 123.
    Selecione um grupo de recursos para novos recursos. Selecione +Criar um grupo de recursos e, em seguida, selecione o nome padrão.
    Selecione uma pilha de runtime. Selecione uma versão LTS da pilha Node.js.
    Selecione um SO. Selecione Linux.
    Selecione uma localização para novos recursos. Escolha uma localização perto de você.
    Selecione um Plano do Serviço de Aplicativo Linux. Selecione Criar Plano de Serviço de Aplicativo. Em seguida, selecione o nome padrão.
    Selecione um tipo de preço. Selecione Gratuito (F1).
    Selecione um recurso do Application Insights para o 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 Aplicativos e clique com o botão direito do mouse em seu novo aplicativo.

  7. Selecione Implantar no Aplicativo Web.

    Captura de tela do Visual Studio Code no Azure Explorer com a opção Implantar no Aplicativo Web realçada.

  8. Selecione a pasta raiz do projeto JavaScript.

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

Quando a implantação terminar, o aplicativo não funcionará 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 seguintes etapas 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 permitem leitura, gravação e, potencialmente, outras permissões.

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

O Conector de Serviço é uma ferramenta que simplifica as conexões autenticadas entre diferentes serviços no Azure. No momento, o Conector de Serviço 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 comando az webapp connection create sql. Este único comando conclui as três etapas mencionadas acima para você.

Criar a identidade gerenciada com o Conector de Serviço

Execute o comando a seguir no Cloud Shell do portal do Azure. O Cloud Shell tem a versão mais recente da CLI do Azure. Substitua as variáveis em <> 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 Serviço de Aplicativo

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

  1. No Visual Studio Code, no Azure Explorer, clique com o botão direito do mouse no Serviço de Aplicativo 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 pelo 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ê deverá 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 de propriedade.

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 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.

Dica

Se você receber um erro de servidor interno 500 durante o teste, isso poderá ser resultante das configurações de rede do banco de dados. Verifique se o servidor lógico foi 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 portal do Azure, pesquise 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 Tem certeza de que deseja excluir... que é aberta, digite o nome do banco de dados para confirmar e selecione Excluir.

Código de exemplo

O código de exemplo desse aplicativo está disponível:

Próximas etapas