Conexión a Azure SQL Database y consulta mediante Node.js y el paquete npm de mssql

Se aplica a:Azure SQL Database

En este inicio rápido se describe cómo conectar una aplicación a una base de datos en Azure SQL Database y realizar consultas mediante Node.js y mssql. En este inicio rápido se sigue el enfoque recomendado sin contraseña para conectarse a la base de datos.

Conexiones sin contraseña para desarrolladores

Las conexiones sin contraseña ofrecen un mecanismo más seguro para acceder a los recursos de Azure. En este artículo se usan los siguientes pasos generales para conectarse a Azure SQL Database mediante conexiones sin contraseña:

  • Prepare el entorno para la autenticación sin contraseña.
    • Para un entorno local: se usa su identidad personal. Esta identidad se puede extraer de un IDE, la CLI u otras herramientas de desarrollo local.
    • Para un entorno de nube: se usa una identidad administrada.
  • Autentíquese en el entorno mediante DefaultAzureCredential de la biblioteca de identidades de Azure para obtener una credencial verificable.
  • Use la credencial verificable para crear objetos de cliente SDK de Azure para el acceso a recursos.

Puede consultar más información sobre las conexiones sin contraseña en Conexiones sin contraseña para servicios de Azure.

Requisitos previos

Configuración del servidor de bases de datos

Las conexiones seguras sin contraseña a Azure SQL Database requieren una cierta configuración de la base de datos. Compruebe la siguiente configuración en el servidor lógico de Azure para conectarse correctamente a Azure SQL Database tanto en el entorno local como en un entorno hospedado:

  1. En el caso de las conexiones de desarrollo local, asegúrese de que el servidor lógico está configurado para permitir que la dirección IP de la máquina local y otros servicios de Azure se conecten:

    • Vaya a la página Redes del servidor.

    • Active o desactive el botón de radio Redes seleccionadas para mostrar opciones de configuración adicionales.

    • Seleccione Agregar la dirección IPv4 de cliente (xx.xx.xx.xx) para agregar una regla de firewall que habilitará las conexiones desde la dirección IPv4 del equipo local. Como alternativa, también puede seleccionar + Agregar una regla de firewall para especificar una dirección IP específica de su elección.

    • Asegúrese de que la casilla Permitir que los servicios y recursos de Azure accedan a este servidor esté seleccionada.

      Captura de pantalla que muestra cómo configurar reglas de firewall.

      Advertencia

      La habilitación de la opción Permitir que los servicios y recursos de Azure accedan a este servidor no es un procedimiento de seguridad recomendado para escenarios de producción. Las aplicaciones reales deben implementar enfoques más seguros, como restricciones de firewall más fuertes o configuraciones de red virtual.

      Puede consultar más información sobre las configuraciones de seguridad de la base de datos en los siguientes recursos:

  2. El servidor también debe tener habilitada la autenticación de Microsoft Entra y tener asignada una cuenta de administrador de Microsoft Entra. Para las conexiones de desarrollo local, la cuenta de administrador de Microsoft Entra debe ser una cuenta que también puede iniciar sesión en Visual Studio o en la CLI de Azure localmente. Puede comprobar si el servidor tiene habilitada la autenticación de Microsoft Entra en la página de Microsoft Entra ID del servidor lógico.

    Captura de pantalla que muestra cómo habilitar la autenticación de Microsoft Entra.

  3. Si usa una cuenta personal de Azure, asegúrese de que ha instalado y configurado Microsoft Entra para Azure SQL Database con el fin de asignar su cuenta como administrador del servidor. Si usa una cuenta corporativa, es probable que Microsoft Entra ID ya esté configurado.

Creación del proyecto

Los pasos de esta sección crean una API REST de Node.js.

  1. Cree un nuevo directorio para el proyecto y vaya a él.

  2. Para inicializar el proyecto, ejecute el siguiente comando en el terminal:

    npm init -y
    
  3. Instale los paquetes necesarios usados en el código de ejemplo de este artículo:

    npm install mssql swagger-ui-express yamljs
    
  4. Instale el paquete de desarrollo usado en el código de ejemplo de este artículo:

    npm install --save-dev dotenv 
    
  5. Abra el proyecto en Visual Studio Code.

    code .
    
  6. Abra el archivo package.json y agregue la siguiente propiedad y valor después de la propiedad name para configurar el proyecto para los módulos ESM.

    "type": "module",
    

Creación de código de aplicación de Express.js

Para crear la aplicación OpenAPI de Express.js, creará varios archivos:

Archivo Descripción
.env.development Archivo de entorno de solo desarrollo local.
index.js Archivo de aplicación principal, que inicia la aplicación Express.js en el puerto 3000.
person.js Archivo API de enrutamiento Express.js /person para controlar las operaciones CRUD.
openapi.js Ruta Express.js /api-docs para la interfaz de usuario del explorador de OpenAPI. La raíz redirige a esta ruta.
openApiSchema.yml Archivo de esquema de OpenAPI 3.0 que define Person API.
config.js Archivo de configuración para leer variables de entorno y construir el objeto de conexión mssql adecuado.
database.js Clase de base de datos para controlar operaciones CRUD de Azure SQL mediante el paquete mssql npm.
./vscode/settings.json Omita los archivos por patrón global durante la implementación.
  1. Cree un archivo index.js y agregue el código siguiente:

    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. Cree un archivo de ruta person.js y agregue el siguiente 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. Crea un archivo de ruta openapi.js y agregue el siguiente código para el explorador de la interfaz de usuario 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. Cree un archivo de esquema openApiSchema.yml y agregue el siguiente código 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
    

Configuración del objeto de conexión mssql

El paquete mssql implementa la conexión a Azure SQL Database proporcionando un ajuste de configuración para un tipo de autenticación.

  1. En Visual Studio Code, cree un archivo config.js y agregue el siguiente código de configuración mssql para autenticarse en Azure SQL Database.

    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. Cree un archivo .env.development para las variables de entorno local y agregue el texto siguiente y actualice con los valores de <YOURSERVERNAME> y <YOURDATABASENAME>.

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

Nota

Los objetos de configuración sin contraseña son seguros para confirmarlos en el control de código fuente, ya que no contienen secretos como nombres de usuario, contraseñas o claves de acceso.

  1. Cree una carpeta .vscode y cree un archivo settings.json en la carpeta.

  2. Agregue lo siguiente para omitir las variables de entorno y las dependencias durante la implementación del archivo .ZIP.

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

Adición del código para conectarse a Azure SQL Database

  1. Cree un archivo database.js y agregue el siguiente 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];
      }
    }
    

Prueba de la aplicación de forma local

La aplicación está lista para probarse localmente. Asegúrese de que ha iniciado sesión en Azure Cloud en Visual Studio Code con la misma cuenta que ha establecido como administrador de la base de datos.

  1. Ejecute la aplicación con el comando siguiente. La aplicación se inicia en el puerto 3000.

    NODE_ENV=development node index.js
    

    La tabla Person se crea en la base de datos al ejecutar esta aplicación.

  2. En un explorador, vaya al explorador de OpenAPI en http://localhost:3000.

  3. En la página de la interfaz de usuario de Swagger, expanda el método POST y seleccione Probar.

  4. Modifique el código JSON de ejemplo para incluir los valores de las propiedades. La propiedad ID se omite.

    Captura de pantalla que muestra cómo probar la API.

  5. Seleccione Ejecutar para agregar un nuevo registro a la base de datos. La API devuelve una respuesta correcta.

  6. Expanda el método GET en la página de la interfaz de usuario de Swagger y seleccione Pruébelo. Seleccione Ejecutar y se devolverá la persona que acaba de crear.

Implementación en Azure App Service

La aplicación está lista para implementarse en Azure. Visual Studio Code puede crear una instancia de Azure App Service e implementar la aplicación en un único flujo de trabajo.

  1. Asegúrese de que la aplicación está detenida.

  2. Inicie sesión en Azure, si aún no lo ha hecho, seleccionando el comando Azure: Iniciar sesión en la nube de Azure en la paleta de comandos (Ctrl + Mayús + P)

  3. En la ventana Azure Explorer de Visual Studio Code, haga clic con el botón derecho en el nodo App Services y seleccione Crear nueva aplicación web (avanzado).

  4. Use la tabla siguiente para crear la instancia de App Service:

    Prompt Valor
    Escriba un nombre único global para la nueva aplicación web. Escriba una indicación como azure-sql-passwordless. Anexe al final una cadena única, como 123.
    Seleccione un grupo de recursos para los nuevos recursos. Seleccione +Crear un nuevo grupo de recursos y, a continuación, seleccione el nombre predeterminado.
    Seleccione una pila del entorno en tiempo de ejecución. Seleccione una versión LTS de la pila de Node.js.
    Seleccione un sistema operativo. Seleccione Linux.
    Seleccione una ubicación para los nuevos recursos. Selección de una ubicación cercana a usted.
    Seleccione un plan de App Service para Linux. Seleccione Crear nuevo plan de App Service. A continuación, seleccione el nombre predeterminado.
    Seleccione un plan de tarifa. Seleccione Gratis (F1).
    Seleccione un recurso de Application Insights para la aplicación. Seleccione Omitir por ahora.
  5. Espere hasta que aparezca la notificación de que su aplicación se ha creado antes de continuar.

  6. En Azure Explorer, expanda el nodo App Services y haga clic con el botón derecho en la nueva aplicación.

  7. Seleccione Implementar en aplicación web.

    Captura de pantalla de Visual Studio Code en el explorador de Azure con la opción Implementar en aplicación web resaltada.

  8. Seleccione la carpeta raíz del proyecto de JavaScript.

  9. Cuando aparezca la ventana emergente de Visual Studio Code, seleccione Implementar.

Cuando finaliza la implementación, la aplicación no funciona correctamente en Azure. Todavía debe configurar la conexión segura entre la instancia de App Service y la base de datos SQL para recuperar los datos.

Conexión de App Service a Azure SQL Database

Los pasos siguientes son necesarios para conectar la instancia de App Service a Azure SQL Database:

  1. Cree una identidad administrada para App Service.
  2. Cree un usuario de SQL Database y asócielo a la identidad administrada de App Service.
  3. Asigne roles de SQL al usuario de la base de datos que le concedan permisos de lectura, escritura y quizá otros permisos.

Hay varias herramientas disponibles para implementar estos pasos:

El conector de servicio es una herramienta que simplifica las conexiones autenticadas entre distintos servicios de Azure. Actualmente, el conector de servicio admite la conexión de una instancia de App Service a una base de datos de Azure SQL a través de la CLI de Azure, con el comando az webapp connection create sql. Este único comando completa los tres pasos mencionados anteriormente.

Creación de la identidad administrada con el conector de servicio

Ejecute el siguiente comando en el Cloud Shell de Azure Portal. Cloud Shell tiene la versión más reciente de la CLI de Azure. Reemplace las variables de <> por sus propios 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

Comprobación de la configuración de la aplicación App Service

Puede comprobar los cambios realizados por el conector de servicio en la configuración de App Service.

  1. En Visual Studio Code, en el explorador de Azure, haga clic con el botón derecho en App Service y seleccione Abrir en el portal.

  2. Vaya a la página Identidad de la instancia de App Service. En la pestaña Asignado por el sistema, el Estado debe establecerse en Activado. Este valor significa que se habilitó una identidad administrada asignada por el sistema para la aplicación.

  3. Vaya a la página Configuración de la instancia de App Service. En la pestaña Configuración de la aplicación, debería ver varias variables de entorno, que ya estaban en el objeto de configuración mssql.

    • AZURE_SQL_SERVER
    • AZURE_SQL_DATABASE
    • AZURE_SQL_PORT
    • AZURE_SQL_AUTHENTICATIONTYPE

    No elimine ni cambie los nombres o valores de propiedad.

Prueba de la aplicación implementada

Vaya a la dirección URL de la aplicación para probar que funciona la conexión a Azure SQL Database. Puede encontrar la dirección URL de la aplicación en la página de información general de App Service.

La persona que creó localmente debe mostrarse en el explorador. ¡Enhorabuena! La aplicación ahora está conectada a Azure SQL Database en entornos locales y hospedados.

Sugerencia

Si recibe un error del servidor interno 500 durante las pruebas, puede deberse a las configuraciones de red de la base de datos. Compruebe que el servidor lógico está configurado con los valores descritos en la sección Configurar la base de datos.

Limpiar los recursos

Cuando haya terminado de trabajar con Azure SQL Database, elimine el recurso para evitar costos no previstos.

  1. En la barra de búsqueda de Azure Portal, busque Azure SQL y seleccione el resultado coincidente.

  2. Busque y seleccione la base de datos en la lista de bases de datos.

  3. En la página Información general de Azure SQL Database, seleccione Eliminar.

  4. En la página Está seguro de que quiere eliminar… de Azure que se abre, escriba el nombre de la base de datos para confirmar y, después, seleccione Eliminar.

Código de ejemplo

El código de ejemplo de esta aplicación está disponible en GitHub.

Pasos siguientes