Entrevista nodejs Flashcards
(139 cards)
¿Cómo trabaja Node.js?
Node.js trabaja con un solo hilo y utiliza un modelo de E/S no bloqueante basado en eventos. Esto significa que solo ejecuta un proceso a la vez, pero puede manejar múltiples operaciones simultáneamente mediante el uso de un bucle de eventos (Event Loop), delegando tareas de E/S o computación intensiva a subprocesos o APIs de sistema.
¿Qué es el modelo Blocking y Non-Blocking en Node.js?
Blocking: La ejecución del programa se detiene hasta que una operación completa su tarea. Por ejemplo, usar métodos síncronos como fs.readFileSync.
Non-Blocking: Permite que el programa continúe ejecutándose sin esperar a que una operación termine, usando métodos asíncronos como fs.readFile.
¿Cuál es la diferencia entre operaciones síncronas y asíncronas?
Síncronas: Bloquean el flujo del programa hasta que se completa la operación.
Asíncronas: Permiten que otras tareas se ejecuten mientras una operación está en curso, delegando su resolución a un callback, promesa o async/await.
¿Qué son los callbacks y cómo se usan en Node.js?
En Node.js, un “callback” es una función que se pasa como argumento a otra función y se ejecuta cuando se completa una operación asincrónica, como leer un archivo o realizar una consulta a la base de datos. Node.js usa callbacks para garantizar que el código no se bloquee durante la espera de estas operaciones.
Ejemplo de lectura de archivo con callback:
```javascript
const fs = require(‘fs’);
fs.readFile(‘archivo.txt’, ‘utf8’, (err, data) => {
if (err) {
console.log(‘Error:’, err);
} else {
console.log(‘Contenido del archivo:’, data);
}
});
~~~
¿Qué es el Event Loop en Node.js?
El Event Loop es el mecanismo que permite a Node.js manejar operaciones asíncronas. Este verifica continuamente si hay tareas en la Callback Queue que deben ser ejecutadas en el Call Stack. Si el Call Stack está vacío, transfiere las tareas de la Callback Queue para su ejecución.
¿Qué es el Call Stack y cómo interactúa con el Event Loop?
El Call Stack es una estructura de datos en la que Node.js mantiene un seguimiento de las funciones que están siendo ejecutadas. Las operaciones se apilan (push) cuando son llamadas y se desapilan (pop) cuando terminan.
El Event Loop solo mueve tareas de la Callback Queue al Call Stack si este último está vacío.
¿Qué es la Callback Queue?
Es una cola donde se almacenan los callbacks listos para ser ejecutados una vez que las operaciones asíncronas correspondientes han terminado. Utiliza un modelo FIFO (First In, First Out).
¿Qué papel juega Libuv en Node.js?
Libuv es una biblioteca escrita en C que abstrae las operaciones de E/S no bloqueantes y provee el Event Loop. Maneja subprocesos, operaciones de archivos, sockets y temporizadores.
¿Cómo manejarías un código asíncrono que se vuelve difícil de leer debido al anidamiento de callbacks?
Este problema se conoce como Callback Hell. Para solucionarlo, podemos:
1. Usar Promesas:
javascript fs.promises.readFile('archivo.txt', 'utf8') .then(data => console.log(data)) .catch(err => console.error(err));
2. Usar
async/await
para un flujo más limpio:javascript async function leerArchivo() { try { const data = await fs.promises.readFile('archivo.txt', 'utf8'); console.log(data); } catch (err) { console.error(err); } } leerArchivo();
¿Qué son las Promesas en Node.js y cómo funcionan?
Las “promesas” son un mecanismo para manejar operaciones asincrónicas en JavaScript. Una promesa representa un valor que puede no estar disponible en el momento de su creación, pero lo estará en el futuro. Tiene tres estados:
- Pending: La promesa está en progreso.
- Resolved: La promesa se resolvió correctamente.
- Rejected: La promesa fue rechazada con un error.
Se puede crear una promesa con el constructor Promise
, y se usa .then()
para manejar el resultado, o .catch()
para manejar errores.
Ejemplo:
```javascript
const miPromesa = new Promise((resolve, reject) => {
const exito = true;
if (exito) {
resolve(‘Operación exitosa’);
} else {
reject(‘Hubo un error’);
}
});
miPromesa
.then((resultado) => console.log(resultado)) // “Operación exitosa”
.catch((error) => console.log(error)); // “Hubo un error”
~~~
¿Qué es async/await y cómo mejora el manejo de código asíncrono?
async/await
es una sintaxis moderna que permite trabajar con Promesas de manera más sencilla y similar a un flujo síncrono.
Ejemplo:
```javascript
async function ejemplo() {
try {
const resultado = await fs.promises.readFile(‘archivo.txt’, ‘utf8’);
console.log(resultado);
} catch (error) {
console.error(error);
}
}
~~~
¿Qué diferencia hay entre setImmediate y process.nextTick?
-
setImmediate
: Agenda la ejecución de una función en la siguiente iteración del Event Loop. -
process.nextTick
: Ejecuta la función inmediatamente después de que se complete la ejecución actual, antes de que el Event Loop procese otras tareas.
Ejemplo:
```javascript
console.log(‘Inicio’);
setImmediate(() => console.log(‘setImmediate’));
process.nextTick(() => console.log(‘nextTick’));
console.log(‘Fin’);
// Output: Inicio, Fin, nextTick, setImmediate
~~~
¿Qué pasa si bloqueas el Event Loop?
Bloquear el Event Loop, por ejemplo, con cálculos intensivos o bucles infinitos, impide que Node.js maneje nuevas solicitudes o complete tareas asíncronas, causando una mala experiencia del usuario.
¿Cómo se comunican los subprocesos en Node.js?
Node.js utiliza el módulo worker_threads
o child_process
para crear subprocesos o procesos secundarios. Estos pueden comunicarse mediante eventos y mensajes, gracias a la serialización de datos con postMessage
y onmessage
.
Ejemplo usando worker_threads:
```javascript
const { Worker } = require(‘worker_threads’);
const worker = new Worker(‘./worker.js’);
worker.postMessage(‘Inicia tarea’);
worker.on(‘message’, (msg) => console.log(msg));
~~~
¿Qué es Express.js y cuáles son sus principales características?
Express.js es un marco de aplicación web minimalista para Node.js que facilita la creación de APIs y servidores web.
Principales características:
1. Rutas (Routing): Manejo sencillo de rutas para diferentes métodos HTTP (GET, POST, etc.).
2. Middleware: Permite manejar solicitudes, respuestas y lógica intermedia.
3. Plantillas: Soporte para motores de plantillas como EJS, Pug o Handlebars.
4. Compatibilidad: Flexible y compatible con múltiples bibliotecas y herramientas.
¿Qué son los middleware en Express.js y cómo funcionan?
Un middleware en Express.js es una función que tiene acceso al objeto de solicitud (req
), al objeto de respuesta (res
) y a la siguiente función middleware en el ciclo de vida de la solicitud. Los middlewares se utilizan para realizar tareas como autenticación, validación de datos, logging, etc.
Ejemplo:
```javascript
const express = require(‘express’);
const app = express();
// Middleware de logging
app.use((req, res, next) => {
console.log(${req.method} ${req.url}
);
next(); // Pasa al siguiente middleware
});
app.get(‘/’, (req, res) => {
res.send(‘Hola, Mundo!’);
});
app.listen(3000, () => console.log(‘Servidor en puerto 3000’));
~~~
¿Cómo se manejan los errores en Express.js?
Express.js maneja errores utilizando middleware de manejo de errores. Un middleware de error tiene cuatro parámetros: err
, req
, res
, y next
.
Ejemplo:
```javascript
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(‘Ocurrió un error!’);
});
~~~
¿Cómo definirías y usarías rutas en Express.js?
Las rutas se definen usando los métodos HTTP (como app.get
, app.post
). También se pueden agrupar con express.Router
.
Ejemplo:
```javascript
app.get(‘/users’, (req, res) => {
res.send(‘Lista de usuarios’);
});
const router = express.Router();
router.post(‘/login’, (req, res) => {
res.send(‘Usuario autenticado’);
});
app.use(‘/auth’, router);
~~~
¿Qué es CORS y cómo lo manejas en Express.js?
CORS (Cross-Origin Resource Sharing) permite que un servidor acepte solicitudes desde dominios diferentes al suyo. Se configura en Express.js usando el middleware cors
.
Ejemplo:
```javascript
const cors = require(‘cors’);
app.use(cors());
~~~
¿Cómo proteges una API en Express.js?
Helmet
(Helmet es una colección de middleware que ayuda a proteger tu aplicación configurando varios encabezados HTTP de seguridad)
```javascript:app.js
const helmet = require(‘helmet’);
app.use(helmet());
// Si solo quieres deshabilitar X-Powered-By
app.disable(‘x-powered-by’);
~~~
TLS/HTTPS
(Transport Layer Security cifra los datos entre el cliente y servidor, protegiendo contra ataques man-in-the-middle)
```javascript:app.js
const https = require(‘https’);
const fs = require(‘fs’);
const options = {
key: fs.readFileSync(‘path/to/key.pem’),
cert: fs.readFileSync(‘path/to/cert.pem’)
};
https.createServer(options, app).listen(443);
~~~
- Gestión de Cookies Seguras
(Las cookies deben configurarse con opciones de seguridad apropiadas para prevenir ataques XSS y CSRF)
```javascript:app.js
const session = require(‘express-session’);
app.use(session({
name: ‘sessionId’, // Evitar el nombre por defecto ‘connect.sid’
secret: ‘tu_secreto_muy_seguro’,
cookie: {
secure: true, // Solo HTTPS
httpOnly: true, // No accesible via JavaScript
domain: ‘tudominio.com’,
path: ‘/’,
expires: new Date(Date.now() + 60 * 60 * 1000) // 1 hora
}
}));
~~~
- Rate Limiting
(Previene ataques de fuerza bruta limitando el número de solicitudes)
```javascript:app.js
const rateLimit = require(‘express-rate-limit’);
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100, // límite de 100 peticiones por ventana
message: ‘Demasiadas peticiones desde esta IP, intente nuevamente más tarde’
});
app.use(‘/api/’, limiter);
~~~
- Validación de Entrada
(Previene inyecciones y ataques XSS validando la entrada del usuario)
```javascript:routes/api.js
const { body, validationResult } = require(‘express-validator’);
app.post(‘/api/user’, [
body(‘email’).isEmail(),
body(‘password’).isLength({ min: 8 }),
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Procesar la petición
});
~~~
- CORS Configuración
(Cross-Origin Resource Sharing controla qué dominios pueden acceder a tu API)
```javascript:app.js
const cors = require(‘cors’);
// Configuración básica
app.use(cors());
// Configuración específica
const corsOptions = {
origin: ‘https://tudominio.com’,
methods: [‘GET’, ‘POST’],
allowedHeaders: [‘Content-Type’, ‘Authorization’],
credentials: true
};
app.use(cors(corsOptions));
~~~
- Prevención de Ataques SQL
(Usar consultas parametrizadas para prevenir inyección SQL)
```javascript:db/queries.js
const { Pool } = require(‘pg’);
const pool = new Pool();
// ❌ MAL (vulnerable a inyección SQL)
const badQuery = SELECT * FROM users WHERE email = '${userInput}'
;
// ✅ BIEN (consulta parametrizada)
const goodQuery = {
text: ‘SELECT * FROM users WHERE email = $1’,
values: [userInput]
};
await pool.query(goodQuery);
~~~
¿Cómo manejarías el almacenamiento de variables de entorno en Node.js?
Usando la biblioteca dotenv
para cargar variables de un archivo .env
al objeto process.env
.
Ejemplo:
```javascript
require(‘dotenv’).config();
console.log(process.env.DB_HOST);
~~~
¿Cómo implementas autenticación con JWT en Express.js?
- Generar un token al iniciar sesión.
- Verificar el token en rutas protegidas.
Ejemplo:
```javascript
const jwt = require(‘jsonwebtoken’);
// Generar un token
app.post(‘/login’, (req, res) => {
const user = { id: 1, username: ‘usuario’ };
const token = jwt.sign(user, ‘clave_secreta’);
res.json({ token });
});
// Middleware para verificar el token
function verificarToken(req, res, next) {
const token = req.headers[‘authorization’];
if (!token) return res.status(403).send(‘Token requerido’);
jwt.verify(token, ‘clave_secreta’, (err, user) => {
if (err) return res.status(403).send(‘Token inválido’);
req.user = user;
next();
});
}
app.get(‘/protegido’, verificarToken, (req, res) => {
res.send(‘Ruta protegida’);
});
~~~
¿Qué son los Streams en Node.js y cómo se relacionan con Express.js?
Los streams en Node.js son flujos de datos que pueden ser leídos o escritos de manera secuencial. En Express.js, se usan para manejar respuestas eficientes, como enviar archivos grandes al cliente.
Ejemplo:
javascript
Copy code
app.get(‘/archivo’, (req, res) => {
const stream = fs.createReadStream(‘archivo_grande.txt’);
stream.pipe(res);
});
¿Cómo manejarías grandes volúmenes de datos en una API construida con Express.js?
- Paginación: Dividir los resultados en porciones manejables.
- Streams: Para procesar datos en partes.
- Cacheo: Usar Redis o memoria para almacenar resultados frecuentes.
-
Compresión: Usar
compression
para reducir el tamaño de las respuestas.
Ejemplo de compresión:
```javascript
const compression = require(‘compression’);
app.use(compression());
~~~