├── src ├── main.js ├── router │ └── users.js ├── db │ └── connection.js ├── app │ └── app.js ├── auth │ └── auth.js ├── jwt │ └── jwt.js ├── models │ └── users.js └── controller │ └── users.js ├── .eslintrc.json ├── package.json ├── docs ├── nodemon.md ├── express-validator.md ├── cors.md ├── dotEnv.md ├── bcrypt.md ├── express.md ├── morgan.md ├── nodeJs.md ├── eslint.md ├── jsonWebToken.md ├── ejemplos │ └── express.md └── mongoose.md ├── .gitignore ├── _README.md └── README.md /src/main.js: -------------------------------------------------------------------------------- 1 | const app = require('./app/app'); 2 | require('dotenv').config(); 3 | 4 | app.listen(process.env.APP_PORT, () => { 5 | console.log(`Server running on port ${process.env.APP_PORT}`); 6 | }); 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es2021": true 6 | }, 7 | "extends": "airbnb-base", 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "rules": { 12 | "no-console": "off" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/router/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { getUsers, singUpUser, singInUser } = require('../controller/users'); 3 | const { verifyToken } = require('../jwt/jwt'); 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/', verifyToken, getUsers); 8 | router.post('/', singUpUser); 9 | router.post('/login', singInUser); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /src/db/connection.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | require('dotenv').config(); 4 | 5 | const dbConnection = async () => { 6 | console.log('conectado a la base de datos'); 7 | try { 8 | await mongoose.connect(process.env.MONGO_URL, { 9 | useNewUrlParser: true, 10 | useUnifiedTopology: true, 11 | dbName: 'blog', 12 | }); 13 | console.log('Conectado a la base de datos'); 14 | } catch (error) { 15 | console.log(error); 16 | throw new Error('Error a la hora de iniciar la base de datos'); 17 | } 18 | }; 19 | 20 | module.exports = { dbConnection }; 21 | -------------------------------------------------------------------------------- /src/app/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cors = require('cors'); 3 | const morgan = require('morgan'); 4 | 5 | const userRouter = require('../router/users'); 6 | const { dbConnection } = require('../db/connection'); 7 | 8 | const app = express(); 9 | 10 | const corsOptions = { 11 | origin: '*', 12 | optionsSuccessStatus: 200, 13 | }; 14 | 15 | app.use(cors(corsOptions)); 16 | app.use(morgan('dev')); 17 | 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: true })); 20 | 21 | dbConnection(); 22 | 23 | app.use('/users', userRouter); 24 | 25 | module.exports = app; 26 | -------------------------------------------------------------------------------- /src/auth/auth.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcrypt'); 2 | 3 | async function hashPassword(next) { 4 | try { 5 | const salt = bcrypt.genSaltSync(10); 6 | const hashedPassword = bcrypt.hashSync(this.password, salt); 7 | this.password = hashedPassword; 8 | next(); 9 | } catch (error) { 10 | next(error); 11 | } 12 | } 13 | 14 | async function comparePassword(password) { 15 | try { 16 | return await bcrypt.compare(password, this.password); 17 | } catch (error) { 18 | throw new Error('Error al comparar las contraseñas'); 19 | } 20 | } 21 | module.exports = { hashPassword, comparePassword }; 22 | -------------------------------------------------------------------------------- /src/jwt/jwt.js: -------------------------------------------------------------------------------- 1 | const jws = require('jsonwebtoken'); 2 | require('dotenv').config(); 3 | 4 | const generateToken = (email, name) => { 5 | const payload = { email, name }; 6 | const options = { expiresIn: '15m' }; 7 | 8 | const token = jws.sign(payload, process.env.JWT_SECRET, options); 9 | return token; 10 | }; 11 | 12 | const verifyToken = (req, res, next) => { 13 | const token = req.headers.authorization; 14 | 15 | if (!token) return res.status(401).json({ error: 'No token provided' }); 16 | 17 | try { 18 | const decoded = jws.verify(token, process.env.JWT_SECRET); 19 | req.user = decoded; 20 | return next(); 21 | } catch (error) { 22 | console.log(error); 23 | return res.status(401).json({ error: 'Invalid token' }); 24 | } 25 | }; 26 | 27 | module.exports = { generateToken, verifyToken }; 28 | -------------------------------------------------------------------------------- /src/models/users.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const { hashPassword, comparePassword } = require('../auth/auth'); 3 | 4 | const { Schema } = mongoose; 5 | 6 | const userSchema = new Schema({ 7 | name: { 8 | type: String, 9 | required: [true, 'El nombre es obligatorio'], 10 | trim: true, 11 | }, 12 | email: { 13 | type: String, 14 | required: [true, 'El email es obligatorio'], 15 | trim: true, 16 | unique: true, 17 | }, 18 | password: { 19 | type: String, 20 | required: [true, 'La contraseña es obligatoria'], 21 | trim: true, 22 | }, 23 | }, { 24 | collection: 'users', 25 | database: 'blog', 26 | }); 27 | 28 | userSchema.pre('save', hashPassword); 29 | 30 | userSchema.methods.comparePassword = comparePassword; 31 | 32 | const Users = mongoose.model('Users', userSchema); 33 | 34 | module.exports = Users; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "bcrypt": "^5.1.0", 4 | "cors": "^2.8.5", 5 | "dotenv": "^16.3.1", 6 | "express": "^4.18.2", 7 | "express-validator": "^7.0.1", 8 | "jsonwebtoken": "^9.0.1", 9 | "mongoose": "^7.4.2" 10 | }, 11 | "name": "express.js-tallreres-donweb", 12 | "description": "Un repositorio para los talleres de Donweb, explicando cómo funciona Express.js", 13 | "version": "1.0.0", 14 | "main": "main.js", 15 | "scripts": { 16 | "start": "node ./src/main.js", 17 | "dev": "nodemon ./src/main.js" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/altaskur/Express.js-Tallreres-Donweb.git" 22 | }, 23 | "author": "Altaskur", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/altaskur/Express.js-Tallreres-Donweb/issues" 27 | }, 28 | "homepage": "https://github.com/altaskur/Express.js-Tallreres-Donweb#readme", 29 | "devDependencies": { 30 | "eslint": "^8.47.0", 31 | "eslint-config-airbnb-base": "^15.0.0", 32 | "eslint-plugin-import": "^2.28.0", 33 | "morgan": "^1.10.0", 34 | "nodemon": "^3.0.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/nodemon.md: -------------------------------------------------------------------------------- 1 | # Nodemon 2 | 3 | Nodemon es una herramienta de desarrollo, encargada de reiniciar el servidor cada vez que guardamos un cambio en nuestro código. Con esto evitamos tener que reiniciar nuestro servidor usando ``ctrl + c`` y ``npm start`` cada vez que hacemos un cambio. 4 | 5 | En las últimas versiones de NodeJs, existe una alternativa nativa a Nodemon, tan sólo debes añadir ``--watch`` al comando de ejecución del servidor, pero, a la hora de crear este taller, ``--watch`` aún estaba en fase experimental y es susceptible a errores, por eso, en este taller vamos a utilizar Nodemon. 6 | 7 | Al igual que con ``--watch``, para ejecutar Nodemon, tan sólo debemos añadir ``nodemon`` al comando de ejecución del servidor, en lugar de usar ``node``, en el archivo ``package.json``: 8 | 9 | ```json 10 | { 11 | "scripts": { 12 | "dev": "nodemon src/index.js" 13 | } 14 | } 15 | ``` 16 | 17 | ## Configuración de Nodemon 18 | 19 | Nodemon, no sólo se limita a reiniciar el servidor, también tiene más funciones cómo mandar una señal a nuestro contenedor o cluster, cambiar el modo de ejecución, limitar que archivos debe controlar e incluso, añadir un delay a la hora de reiniciar el servidor. Esta configuraciones escapan del alcance de este taller, pero si quieres saber más sobre ellas, puedes consultar la [documentación oficial](https://www.npmjs.com/package/nodemon). 20 | -------------------------------------------------------------------------------- /src/controller/users.js: -------------------------------------------------------------------------------- 1 | const { generateToken } = require('../jwt/jwt'); 2 | const Users = require('../models/users'); 3 | 4 | const getUsers = async (req, res) => { 5 | try { 6 | const users = await Users.find(); 7 | res.status(200).json({ users }); 8 | } catch (error) { 9 | console.log(error); 10 | res.status(500).json({ error: 'Ocurrió un error durante la solicitud' }); 11 | } 12 | }; 13 | 14 | const singUpUser = async (req, res) => { 15 | try { 16 | const { name, email, password } = req.body; 17 | const user = new Users({ name, email, password }); 18 | await user.save(); 19 | 20 | res.status(200).json({ user }); 21 | } catch (error) { 22 | console.log(error); 23 | res.status(500).json({ error: 'Ocurrió un error durante la solicitud' }); 24 | } 25 | }; 26 | 27 | const singInUser = async (req, res) => { 28 | try { 29 | const { email, password } = req.body; 30 | const user = await Users.findOne({ email }); 31 | 32 | if (!user) return res.status(404).json({ error: 'Usuario no encontrado' }); 33 | 34 | const isMatch = await user.comparePassword(password); 35 | if (!isMatch) return res.status(401).json({ error: 'Contraseña incorrecta' }); 36 | 37 | const token = generateToken(user.email, user.name); 38 | return res.status(200).json({ token }); 39 | } catch (error) { 40 | console.log(error); 41 | return res.status(500).json({ error: 'Ocurrió un error durante la solicitud' }); 42 | } 43 | }; 44 | 45 | module.exports = { getUsers, singUpUser, singInUser }; 46 | -------------------------------------------------------------------------------- /docs/express-validator.md: -------------------------------------------------------------------------------- 1 | # Express-validator 2 | 3 | - [Express-validator](#express-validator) 4 | - [¿Qué es sanitizar?](#qué-es-sanitizar) 5 | - [Desventajas de usar Express-validator](#desventajas-de-usar-express-validator) 6 | - [Ventajas de usar Express-validator](#ventajas-de-usar-express-validator) 7 | - [Ejemplos de uso](#ejemplos-de-uso) 8 | 9 | Express-validator es una librería que nos ayuda a validar los datos que recibimos en las peticiones HTTP, sanitizarlos y escaparlos, haciendo que nuestra aplicación sea más segura. 10 | 11 | ## ¿Qué es sanitizar? 12 | 13 | Sanitizar es un proceso que nos ayuda a limpiar los datos que recibimos en las peticiones HTTP, por ejemplo, si recibimos un texto, podemos eliminar los espacios en blanco, o si recibimos un número, podemos eliminar los caracteres que no sean números. Evitando inyecciones de código. 14 | 15 | ## Desventajas de usar Express-validator 16 | 17 | - No es muy eficiente, a la hora de validar gran cantidad de datos. 18 | - No es muy flexible, a la hora de validar datos complejos. 19 | 20 | ## Ventajas de usar Express-validator 21 | 22 | - Tiene una gran integración con express. 23 | - Es muy fácil de usar. 24 | 25 | ## Ejemplos de uso 26 | 27 | ```js 28 | const { body, validationResult } = require('express-validator'); 29 | 30 | const getUsers = async (req, res) => { 31 | body('name').isLength({ min: 3 }).trim().escape().withMessage('El nombre debe tener al menos 3 caracteres'); 32 | body('email').isEmail().trim().normalizeEmail().withMessage('El email no es válido'); 33 | body('age').isNumeric().trim().escape().toInt().withMessage('La edad debe ser un número'); 34 | const errors = validationResult(req); 35 | if (!errors.isEmpty()) { 36 | return res.status(400).json(errors); 37 | } 38 | 39 | const users = await User.find(); 40 | res.json(users); 41 | }; 42 | ``` 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | 93 | # Gatsby files 94 | .cache/ 95 | # Comment in the public line in if your project uses Gatsby and not Next.js 96 | # https://nextjs.org/blog/next-9-1#public-directory-support 97 | # public 98 | 99 | # vuepress build output 100 | .vuepress/dist 101 | 102 | # vuepress v2.x temp and cache directory 103 | .temp 104 | .cache 105 | 106 | # Docusaurus cache and generated files 107 | .docusaurus 108 | 109 | # Serverless directories 110 | .serverless/ 111 | 112 | # FuseBox cache 113 | .fusebox/ 114 | 115 | # DynamoDB Local files 116 | .dynamodb/ 117 | 118 | # TernJS port file 119 | .tern-port 120 | 121 | # Stores VSCode versions used for testing VSCode extensions 122 | .vscode-test 123 | .vscode 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* -------------------------------------------------------------------------------- /docs/cors.md: -------------------------------------------------------------------------------- 1 | # Cors 2 | 3 | - [Cors](#cors) 4 | - [¿Cómo funciona?](#cómo-funciona) 5 | - [¿Cómo configurar CORS?](#cómo-configurar-cors) 6 | - [Instalación](#instalación) 7 | - [Uso](#uso) 8 | - [Configuración](#configuración) 9 | - [origin](#origin) 10 | - [method](#method) 11 | - [headers](#headers) 12 | 13 | Cors de forma simplificada, es una política de seguridad, que permite o restringe el acceso a recursos de un servidor, desde un dominio especifico. 14 | 15 | ## ¿Cómo funciona? 16 | 17 | Cuando un navegador hace una petición a un servidor, que está en a un dominio diferente al que se encuentra, el servidor responde con un header, el servidor responde con una solicitud, con un encabezado, que indica si el recurso puede ser accedido o no. 18 | 19 | En este encabezado, indica los dominios que pueden acceder a los recursos, los métodos que pueden ser usados para acceder a los recursos, entre otros. 20 | 21 | ## ¿Cómo configurar CORS? 22 | 23 | Podemos añadir encabezados a las respuestas de nuestro servidor, de una manera muy sencilla, con el paquete cors de npm. 24 | 25 | ### Instalación 26 | 27 | ```bash 28 | npm install cors 29 | ``` 30 | 31 | ### Uso 32 | 33 | Como todo middelware, tan solo debemos de añadirlo a nuestra instancia de express, con el método use. 34 | `app.use(cors(corsOptions));` 35 | Recomiendo guardar la configuración en una constante, para que sea más fácil de leer. 36 | 37 | ```js 38 | const express = require('express'); 39 | const cors = require('cors'); 40 | 41 | const app = express(); 42 | corsOptions = { 43 | origin: ["http://example.com", "https://example1.com"], 44 | method: ['GET', 'POST'], 45 | headers: ["Authorization", "Content-Type"] 46 | } 47 | 48 | app.use(cors(corsOptions)); 49 | 50 | app.listen(3000, () => { 51 | console.log('Server on port 3000'); 52 | }); 53 | ``` 54 | 55 | ## Configuración 56 | 57 | Podemos configurar el paquete cors, para que solo permita el acceso a los recursos, desde un dominio especifico. 58 | 59 | analizaremos los parámetros más importantes. 60 | 61 | ### origin 62 | 63 | Este parámetro, nos permite indicar los dominios que pueden acceder a los recursos, podemos pasar un string, un array de strings, una expresión regular o una función. 64 | 65 | ```js 66 | corsOptions = { 67 | origin: ["http://example.com", "https://example1.com"], 68 | } 69 | ``` 70 | 71 | ### method 72 | 73 | Este parámetro, nos permite indicar los métodos que pueden ser usados para acceder a los recursos, podemos pasar un string, un array de strings, una expresión regular o una función. 74 | 75 | ```js 76 | corsOptions = { 77 | method: ['GET', 'POST'], 78 | } 79 | ``` 80 | 81 | ### headers 82 | 83 | Este parámetro, nos permite indicar los encabezados que pueden ser usados, por ejemplo authorization. 84 | 85 | ```js 86 | corsOptions = { 87 | headers: ["Authorization", "Content-Type"] 88 | } 89 | ``` 90 | 91 | Recuerda, contra más restrictivo sea la configuración de CORS, más seguro será tu servidor. 92 | -------------------------------------------------------------------------------- /docs/dotEnv.md: -------------------------------------------------------------------------------- 1 | # DotEnv 2 | 3 | - [DotEnv](#dotenv) 4 | - [¿Qué es DotEnv?](#qué-es-dotenv) 5 | - [Variables de entorno](#variables-de-entorno) 6 | - [¿Por qué usar variables de entorno?](#por-qué-usar-variables-de-entorno) 7 | - [Instalación](#instalación) 8 | - [Uso de DotEnv](#uso-de-dotenv) 9 | - [.env](#env) 10 | 11 | ## ¿Qué es DotEnv? 12 | 13 | DotEnv es una librería que nos permite acceder a las variables de entorno de nuestro sistema operativo. 14 | 15 | ## Variables de entorno 16 | 17 | ¿Que son las variables de entorno?, son variables que define el Sistema Operativo, que son usadas para el correcto uso del sistema, donde se almacenan datos cómo la rutas de los archivos, datos de la sesión, etc. También pueden ser definidas en un programa o en un fichero de configuración. En este caso 18 | las guardaremos en el fichero `.env` que se encuentra en la raíz del proyecto. 19 | 20 | ## ¿Por qué usar variables de entorno? 21 | 22 | Cuándo necesitamos guardar datos sensibles de nuestra aplicación cómo claves de acceso, direcciones de servidores o configuraciones cómo los puertos, vamos a necesitar guardarlas fuera de nuestro código fuente. Un caso muy común es cuando tenemos que subir nuestro código a un repositorio público cómo GitHub, ya que si subimos nuestras claves de acceso, cualquiera podría acceder a ellas y hacer un mal uso de ellas. 23 | 24 | También aislar estos datos del código fuente, nos permite tener una mayor escalabilidad o mantenimiento, sin tener que modificar el código fuente. Por último seguridad, ya que si alguien accede a nuestro código fuente, no podrá acceder a nuestros datos sensibles. 25 | 26 | ## Instalación 27 | 28 | ```bash 29 | npm install dotenv 30 | ``` 31 | 32 | ## Uso de DotEnv 33 | 34 | Para poder usar DotEnv, aparte de importar la librería, tenemos que llamar a ``process.env`` para poder acceder a las variables de entorno. Allí tendremos todas las variables de entorno del sistema y las que hayamos definido en nuestro fichero `.env`. 35 | 36 | ```js 37 | const dotenv = require('dotenv'); 38 | dotenv.config(); 39 | 40 | console.log(process.env.APP_PORT); 41 | ``` 42 | 43 | ## .env 44 | 45 | En el fichero `.env` guardaremos todas las variables de entorno que necesitemos. Para definir una variable de entorno, escribiremos el nombre de la variable, seguido de un igual y el valor de la variable. No es necesario poner comimos ni punto y coma ni nada por el estilo. 46 | 47 | ```env 48 | APP_PORT=3000 49 | DB_HOST=localhost 50 | ``` 51 | 52 | Para acceder a las variables de entorno, usaremos `process.env` seguido del nombre de la variable. 53 | 54 | ```js 55 | const dotenv = require('dotenv'); 56 | dotenv.config(); 57 | 58 | console.log(process.env.APP_PORT); 59 | ``` 60 | 61 | También podemos usar destructuring para acceder a las variables de entorno. 62 | 63 | ```js 64 | const dotenv = require('dotenv'); 65 | dotenv.config(); 66 | 67 | const { APP_PORT, DB_HOST } = process.env; 68 | 69 | console.log(APP_PORT); 70 | console.log(DB_HOST); 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/bcrypt.md: -------------------------------------------------------------------------------- 1 | # bcrypt 2 | 3 | - [bcrypt](#bcrypt) 4 | - [¿Qué es un hash?](#qué-es-un-hash) 5 | - [Blowfish](#blowfish) 6 | - [Funcionamiento de Bcrypt](#funcionamiento-de-bcrypt) 7 | - [Número de rondas](#número-de-rondas) 8 | - [Salt](#salt) 9 | - [Ejemplo de uso](#ejemplo-de-uso) 10 | - [Crear un hash](#crear-un-hash) 11 | - [Comprobar un hash](#comprobar-un-hash) 12 | 13 | Bcrypt es una librería, que nos permite generar hashes de contraseñas, para almacenarlas en nuestra base de datos. 14 | 15 | ## ¿Qué es un hash? 16 | 17 | Un hash es una función matemática que nos genera un string de longitud fija, a partir de un string de longitud variable. Por ejemplo, si partimos de la palabra `hola`, cómo resultado obtendremos `5d41402abc4b2a76b9719d911017c592`, aunque cambiemos a `hola mundo`, el resultado siempre será una cadena de carácteres con la misma longitud. 18 | 19 | Esto nos permite almacenar contraseñas de forma segura, ya que, aunque alguien consiga acceder a nuestra base de datos, no podrá obtener la contraseña original, ya que, no existe una función matemática que nos permita obtener el string original a partir del hash, además de que es imposible saber la longitud del string original. 20 | 21 | En cambio si podemos comprobar si el String proporcionado es igual al hash guardado, ya que, al aplicar la función matemática, obtendremos el mismo resultado. 22 | 23 | ## Blowfish 24 | 25 | Bcrypt utiliza el algoritmo Blowfish, que es un algoritmo de cifrado simétrico y de generación lenta, es decir, utiliza la misma clave para cifrar y descifrar y además dificulta el uso de GPUs para romper la seguridad del mismo. Este algoritmo fue diseñado por Bruce Schneier en 1993, y fue publicado en 1994. 26 | Este algoritmo es muy utilizado en la actualidad, ya que, es muy seguro, y es utilizado en protocolos cómo SSH, SSL, o TLS, pese a tener 30 años de antigüedad, resulta muy estable y seguro. 27 | 28 | ## Funcionamiento de Bcrypt 29 | 30 | Para generar un hash con Bcrypt, utilizamos el método `hashSync`, que recibe dos parámetros, el primero es el string que queremos cifrar, y el segundo es el número de rondas que queremos aplicar al algoritmo. 31 | 32 | ## Número de rondas 33 | 34 | El número de rondas, es el número de veces que se va a aplicar el algoritmo, es decir, aplicara el algoritmo usando cómo entrada el resultado de la anterior ejecución. añadiendo y modificando los datos variables o `salt` en cada ejecución. 35 | 36 | ## Salt 37 | 38 | El salt, es un dato variable que se añade a la entrada del algoritmo, para que el resultado sea diferente en cada ejecución, de esta forma, aunque la entrada sea la misma, el resultado será diferente. 39 | 40 | El salt, se genera de forma aleatoria, y se añade al hash, de esta forma, cuando queramos comprobar si un string es igual al hash, podemos obtener el salt del hash, y aplicarlo al string, para obtener el hash del string, y comprobar si es igual al hash guardado. 41 | 42 | ## Ejemplo de uso 43 | 44 | Ahora que ya sabemos el funcionamiento de Bcrypt, vamos a ver cómo utilizarlo en nuestro proyecto. 45 | 46 | ### Crear un hash 47 | 48 | ```js 49 | const bcrypt = require('bcrypt'); 50 | 51 | const password = '1234'; 52 | const saltRounds = 10; 53 | 54 | const hash = bcrypt.hashSync(password, saltRounds); 55 | 56 | console.log(hash); // $2b$10$Z8Z1Z1Z1Z1Z1Z1Z1Z1Z1Z 57 | ``` 58 | 59 | Importamos al proyecto la librería y creamos un texto de ejemplo, en este caso usaremos `1234`, 60 | 61 | Después tenemos que indicar el número de rondas que aplicaremos, de normal se suele usar 10, pero podemos usar un número mayor, pero ten en cuenta, al mayor número de rondas, más tardará en generar el hash. 62 | 63 | ### Comprobar un hash 64 | 65 | ```js 66 | const bcrypt = require('bcrypt'); 67 | 68 | const password = '1234'; 69 | const hash = '$2b$10$Z8Z1Z1Z1Z1Z1Z1Z1Z1Z1Z'; 70 | 71 | const isCorrect = bcrypt.compareSync(password, hash); 72 | 73 | console.log(isCorrect); // true 74 | ``` 75 | 76 | En este caso sólo tenemos que indicar el string que queremos comprobar, y el hash que queremos comprobar, y nos devolverá un booleano indicando si el string es igual al hash. 77 | 78 | Cómo puedes ver es una librería muy sencilla de utilizar, y nos permite almacenar las contraseñas de forma segura en nuestra base de datos, ya no tienes escusa para no utilizarla. 79 | -------------------------------------------------------------------------------- /docs/express.md: -------------------------------------------------------------------------------- 1 | # Express 2 | 3 | - [Express](#express) 4 | - [¿Qué es Express?](#qué-es-express) 5 | - [Middelwares](#middelwares) 6 | - [Routing](#routing) 7 | - [Funciones de Express](#funciones-de-express) 8 | - [express()](#express-1) 9 | - [app.listen()](#applisten) 10 | - [app.use()](#appuse) 11 | - [app.get() / app.post() / app.put() / app.delete()](#appget--apppost--appput--appdelete) 12 | - [app.render()](#apprender) 13 | - [app.set()](#appset) 14 | - [Propiedades de una petición HTTP](#propiedades-de-una-petición-http) 15 | - [Propiedades de una respuesta HTTP](#propiedades-de-una-respuesta-http) 16 | 17 | ## ¿Qué es Express? 18 | 19 | Express es el framework más famoso de node.js y la base de 20 | la mayoría de los frameworks de manejo de peticiones http, está basado en la librería Sinatra de Ruby. 21 | 22 | Nos permite atender y procesar las peticiones HTTP, utilizar distintos puertos para atenderlas y el uso de motores de renderización (plantillas) para responderlas. 23 | 24 | En sí, el framework es bastante simple, pero existen una serie de librerías (Middelwares) que nos permiten abordar la mayorías de los casos. 25 | 26 | En si nos permite tener un servidor de HTTP, bastante liviano dentro de lo que es un sistema basado en Node y fácilmente escalable. 27 | 28 | ## Middelwares 29 | 30 | Un concepto que vas a escuchar a lo largo del desarrollo de cualquier aplicación basada en Express, se considera middleware a todas aquellas funciones que tienen acceso a los objetos de solicitud y respuesta (res/req) de una petición HTTP, de tal manera que podemos modificar estos mismos antes de que lleguen a su destino final. 31 | 32 | ## Routing 33 | 34 | Al igual que sistemas de Frontend basados en JS, el sistema de rutas, nos permite ejecutar distintas funciones según la ruta de la aplicación Ej: `localhost/ruta`, Express las maneja de una forma especialmente sencilla, que veremos más adelante. 35 | 36 | ## Funciones de Express 37 | 38 | ### express() 39 | 40 | Es la función que nos permite crear una aplicación de Express, la cual nos permite utilizar todas las funciones que nos provee el framework. 41 | 42 | ```js 43 | const express = require('express'); 44 | const app = express(); 45 | ``` 46 | 47 | ### app.listen() 48 | 49 | Es la función que nos permite crear un servidor de Express, el cual nos permite atender las peticiones HTTP. 50 | 51 | ```js 52 | const express = require('express'); 53 | const app = express(); 54 | 55 | app.listen(3000, () => { 56 | console.log('Server in port 3000'); 57 | }); 58 | ``` 59 | 60 | ### app.use() 61 | 62 | Es la función que nos permite utilizar un middleware, el cual nos permite modificar las peticiones HTTP antes de que lleguen a su destino final. 63 | 64 | ```js 65 | const express = require('express'); 66 | const app = express(); 67 | 68 | app.use(express.json()); 69 | ``` 70 | 71 | ### app.get() / app.post() / app.put() / app.delete() 72 | 73 | Es la función que nos permite crear una ruta de tipo GET, la cual nos permite ejecutar una función cuando se haga una petición de tipo GET a la ruta especificada. 74 | 75 | ```js 76 | const express = require('express'); 77 | const app = express(); 78 | 79 | app.get('/', (req, res) => { 80 | res.send('Hello World'); 81 | }); 82 | ``` 83 | 84 | ### app.render() 85 | 86 | Es la función que nos permite renderizar una plantilla, la cual nos permite enviar una respuesta HTML al cliente, aunque esta característica no la vamos a ver a lo largo del curso, es importante que la conozcas. 87 | 88 | ```js 89 | const express = require('express'); 90 | const app = express(); 91 | 92 | app.get('/', (req, res) => { 93 | res.render('index'); 94 | }); 95 | ``` 96 | 97 | ### app.set() 98 | 99 | Es la función que nos permite configurar una variable de entorno, la cual nos permite configurar distintas variables de entorno de nuestra aplicación. 100 | 101 | ```js 102 | const express = require('express'); 103 | const app = express(); 104 | 105 | app.set('port', 3000); 106 | ``` 107 | 108 | ## Propiedades de una petición HTTP 109 | 110 | - **req.params**: Nos permite acceder a los parámetros de la ruta. 111 | - **req.query**: Nos permite acceder a los parámetros de la url. 112 | - **req.body**: Nos permite acceder al cuerpo de la petición. 113 | - **req.headers**: Nos permite acceder a los headers de la petición. 114 | 115 | ## Propiedades de una respuesta HTTP 116 | 117 | - **res.send()**: Nos permite enviar una respuesta de tipo texto. 118 | - **res.json()**: Nos permite enviar una respuesta de tipo JSON. 119 | - **res.render()**: Nos permite enviar una respuesta de tipo HTML. 120 | - **res.redirect()**: Nos permite redireccionar a otra ruta. 121 | - **res.status()**: Nos permite enviar una respuesta con un código de estado. 122 | -------------------------------------------------------------------------------- /_README.md: -------------------------------------------------------------------------------- 1 | 2 | # Repositorio para los talleres de Donweb 3 | 4 | Un repositorio para seguir el taller de NodeJS y Express para Donweb 5 | 6 | - [Repositorio para los talleres de Donweb](#repositorio-para-los-talleres-de-donweb) 7 | - [Estructura del proyecto](#estructura-del-proyecto) 8 | - [Descripción](#descripción) 9 | - [src](#src) 10 | - [app](#app) 11 | - [db](#db) 12 | - [jwt](#jwt) 13 | - [models](#models) 14 | - [Routers](#routers) 15 | - [main](#main) 16 | - [.env](#env) 17 | - [.eslintrc](#eslintrc) 18 | - [.gitignore](#gitignore) 19 | - [Variables de entorno](#variables-de-entorno) 20 | - [Instalación del proyecto](#instalación-del-proyecto) 21 | - [Configuración de eslint](#configuración-de-eslint) 22 | - [Despliegue en local](#despliegue-en-local) 23 | - [Referencia de la api](#referencia-de-la-api) 24 | - [Get](#get) 25 | - [Get por id](#get-por-id) 26 | - [Post insertamos nuevo elemento](#post-insertamos-nuevo-elemento) 27 | - [Post Eliminamos el elemento](#post-eliminamos-el-elemento) 28 | - [Autores](#autores) 29 | 30 | ## Estructura del proyecto 31 | 32 | - src 33 | - app 34 | - db 35 | - jwt 36 | - models 37 | - router 38 | - main.js 39 | - .env 40 | - .eslintrc.json 41 | - .gitignore 42 | 43 | ### Descripción 44 | 45 | #### src 46 | 47 | Albergará toda la lógica del proyecto 48 | 49 | #### app 50 | 51 | Encontraremos toda la configuración de express, cómo las rutas y los middleware a utilizar 52 | 53 | #### db 54 | 55 | Encontraremos la configuración de conexión con la base de datos 56 | 57 | #### jwt 58 | 59 | Esta las funciones de los JSON Web Tokens 60 | 61 | #### models 62 | 63 | La estructura de la base de datos (Schemas) 64 | 65 | #### Routers 66 | 67 | la lógica de cada una de las rutas del proyecto 68 | 69 | #### main 70 | 71 | El punto de partida de nuestra app 72 | 73 | #### .env 74 | 75 | Las variables de entorno de nuestro proyecto 76 | 77 | #### .eslintrc 78 | 79 | La configuración de eslint 80 | 81 | #### .gitignore 82 | 83 | Los archivos que no queremos que se suban a nuestro repositorio 84 | 85 | ## Variables de entorno 86 | 87 | Para poder ejecutar el proyecto necesitamos crear un archivo `.env` en la raíz del proyecto, con las siguientes variables de entorno 88 | 89 | `API_KEY` 90 | 91 | `ANOTHER_API_KEY` 92 | 93 | `MONGO_URL` 94 | 95 | ## Instalación del proyecto 96 | 97 | Para instalar el proyecto vamos a usar NPM cómo gestor de librerías en dos tandas, 98 | primero las dependencias necesarias para el proyecto 99 | 100 | ```bash 101 | npm i bcrypt, cors, dotenv, jsonwebtoken, express-validator, mongoose, express 102 | ``` 103 | 104 | Por último, vamos a instalar las dependencias que nos van ayudar con el desarrollo del mismo 105 | 106 | ```bash 107 | npm i -D nodemon, morgan, eslint 108 | ``` 109 | 110 | ### Configuración de eslint 111 | 112 | Para configurar eslint vamos a ejecutar el siguiente comando 113 | 114 | ```bash 115 | npm init @eslint/config 116 | ``` 117 | 118 | Y vamos a seguir los pasos que nos indica el asistente 119 | 120 | - To check syntax, find problems, and enforce code style 121 | - CommonJS (require/exports) 122 | - None of these 123 | - No 124 | - Node 125 | - Use a popular style guide 126 | - Airbnb: 127 | - JSON 128 | - Yes 129 | - npm 130 | 131 | ## Despliegue en local 132 | 133 | Para lanzar el proyecto el local tenemos que seguir estos pasos: 134 | 135 | Clonamos el repositorio 136 | 137 | ```bash 138 | git clone https://github.com/altaskur/https://github.com/altaskur/Express.js-Talleres-Donweb.git 139 | ``` 140 | 141 | Vamos al directorio del proyecto 142 | 143 | ```bash 144 | cd Express.js-Talleres-Donweb.git 145 | ``` 146 | 147 | Instalamos las dependencias 148 | 149 | ```bash 150 | npm install 151 | ``` 152 | 153 | Iniciamos el servidor 154 | 155 | ```bash 156 | npm run start 157 | ``` 158 | 159 | ## Referencia de la api 160 | 161 | ### Get 162 | 163 | ```http 164 | GET /api/ 165 | ``` 166 | 167 | | Parámetro | Tipo | Descripción | 168 | | :-------- | :------- | :------------------------- | 169 | | `api_key` | `string` | **Required**. Tu API key | 170 | 171 | ### Get por id 172 | 173 | ```http 174 | GET /api/${id} 175 | ``` 176 | 177 | | Parámetro | Tipo | Descripción | 178 | | :-------- | :------- | :-------------------------------- | 179 | | `api_key` | `string` | **Required**. Tu API key | 180 | | `id` | `string` | **Required**. id del elemento a obtener | 181 | 182 | ### Post insertamos nuevo elemento 183 | 184 | ```http 185 | POST /api/ 186 | ``` 187 | 188 | | Parámetro | Tipo | Descripción | 189 | | :-------- | :------- | :-------------------------------- | 190 | | `api_key` | `string` | **Required**. Tu API key | 191 | 192 | ### Post Eliminamos el elemento 193 | 194 | ```http 195 | DELETE /api/${id} 196 | ``` 197 | 198 | | Parámetro | Tipo | Descripción | 199 | | :-------- | :------- | :-------------------------------- | 200 | | `api_key` | `string` | **Required**. Tu API key | 201 | | `id` | `string` | **Required**. id del elemento a obtener | 202 | 203 | ## Autores 204 | 205 | - [@altaskur](https://github.com/altaskur) 206 | -------------------------------------------------------------------------------- /docs/morgan.md: -------------------------------------------------------------------------------- 1 | # Morgan 2 | 3 | - [Morgan](#morgan) 4 | - [¿Cómo usar Morgan en Express?](#cómo-usar-morgan-en-express) 5 | - [Configuraciones preestablecidas](#configuraciones-preestablecidas) 6 | - [Personalizar configuración](#personalizar-configuración) 7 | - [Tokens predefinidos](#tokens-predefinidos) 8 | - [Tokens personalizados](#tokens-personalizados) 9 | 10 | Morgan es un middelware de express, que nos produce un Log, de las peticiones request 11 | que recibimos, seria igual al hacer un console.log de cada una de las peticiones. 12 | 13 | Tenemos varias plantillas prefiguradas para mostrar información, pero también 14 | podemos personalizarlo completamente. 15 | 16 | ## ¿Cómo usar Morgan en Express? 17 | 18 | Para utilizar Morgan en Express debemos usar la función ``app.use()`` 19 | express, indicándole que vamos a utilizar un Middelware, dentro de nuestra instancia de Express. 20 | 21 | ```js 22 | const express = require('express'); 23 | const app = express(); 24 | 25 | app.use(morgan('dev')); 26 | 27 | app.listen(3000, () => { 28 | console.log(`Server running on port 3000`); 29 | }); 30 | 31 | ``` 32 | 33 | ## Configuraciones preestablecidas 34 | 35 | - combined: Lo configura con el formato estándar combinado de Apache 36 | - common: Utiliza el formato estándar de Apache 37 | - dev: Utiliza un código base de colores. 38 | - short: Limita algún dato. 39 | - tiny: Un formato corto y simple con el tiempo de reacción. 40 | 41 | ```js 42 | //TODO MOSTRAR salida de consola 43 | app.use(morgan('combined')); 44 | // ::ffff:127.0.0.1 - - [11/Aug/2023:19:08:22 +0000] 45 | // "GET /users/?id=hola HTTP/1.1" 200 241 "-" 46 | // "Thunder Client (https://www.thunderclient.com)" 47 | app.use(morgan('common')); 48 | // ::ffff:127.0.0.1 - - [11/Aug/2023:19:09:16 +0000] 49 | // "GET /users/?id=hola HTTP/1.1" 200 241 50 | app.use(morgan('dev')); 51 | // GET /users/?id=hola 200 15.932 ms - 241 52 | app.use(morgan('short')); 53 | // ::ffff:127.0.0.1 - GET /users/?id=hola HTTP/1.1 200 241 - 10.139 ms 54 | app.use(morgan('tiny')); 55 | // GET /users/?id=hola 200 241 - 13.740 ms 56 | ``` 57 | 58 | ## Personalizar configuración 59 | 60 | Ahora que hemos visto la configuración predeterminada ahora vamos 61 | a ver cómo podemos personalizarla. 62 | 63 | para ello vamos a nos vamos a introducir el concepto de token, 64 | que en este caso usamos la palabra token, como una representación de una entidad más grande que es interpretada dentro de una cadena o String. de esta manera podemos indicarle a Morgan 65 | cómo queremos visualizar el Log de eventos. 66 | 67 | Muy similar a bash o otros lenguajes de terminal, debemos establecer un carácter para definir el comienzo del token, en el caso de Morgan, tenemos que usar el carácter `:` `:method`. 68 | 69 | ### Tokens predefinidos 70 | 71 | Existen una serie de tokens predefinidos, te dejo una lista de los más usados. 72 | 73 | - :date - Fecha actual. 74 | - :method - Método HTTP de la solicitud (GET, POST, etc.). 75 | - :url - URL solicitada. 76 | - :status - Código de estado de la respuesta HTTP. 77 | - :response-time - Tiempo de respuesta en milisegundos. 78 | - :remote-addr - Dirección IP del cliente. 79 | - :remote-user - Usuario remoto (generalmente para autenticación básica). 80 | - :http-version - Versión del protocolo HTTP. 81 | - :referrer - Referencia del encabezado "Referer" en la solicitud HTTP. 82 | - :user-agent - User-Agent del cliente (información sobre el navegador o cliente HTTP). 83 | - :req[header] - Valor de un encabezado específico en la solicitud. Reemplaza "header" por el nombre del encabezado (por ejemplo, :req[User-Agent]). 84 | - :res[header] - Valor de un encabezado específico en la respuesta. Reemplaza "header" por el nombre del encabezado (por ejemplo, :res[Content-Type]). 85 | 86 | Teniendo esto en cuenta vamos a crear nuestro log personalizado 87 | 88 | ```js 89 | morgan(':method :url :status :res[content-length] - :response-time ms') 90 | ``` 91 | 92 | Aquí te dejo un ejemplo de cual sería la configuración manual 93 | de cada uno de las configuraciones preestablecidas de Morgan 94 | 95 | ```js 96 | // Combined 97 | ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' 98 | 99 | // Standard 100 | ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length' 101 | 102 | // dev 103 | ':method :url :status :response-time ms - :res[content-length]' 104 | 105 | // Short 106 | ':remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms' 107 | 108 | // Tiny 109 | ':method :url :status :res[content-length] - :response-time ms' 110 | ``` 111 | 112 | ### Tokens personalizados 113 | 114 | También nos brinda la opción de crear tokens personalizados, para ello tenemos que utilizar 115 | la función token de morgan `morgan.token`. 116 | 117 | Ten en cuenta que si escribimos un token personalizado con el mismo nombre 118 | que uno existente, este último se sobrescribirá. 119 | 120 | ```js 121 | morgan.token('user', (req, res) => { 122 | return req.headers['x-username']; 123 | }); 124 | 125 | morgan.token('id', (req, res) => { 126 | return req.headers['x-id']; 127 | }); 128 | ``` 129 | 130 | Ahora podemos usar estos tokens personalizados en nuestra configuración. 131 | 132 | ```js 133 | morgan(':user :id') 134 | ``` 135 | -------------------------------------------------------------------------------- /docs/nodeJs.md: -------------------------------------------------------------------------------- 1 | # Node.js 2 | 3 | - [Node.js](#nodejs) 4 | - [¿Qué es Node.js?](#qué-es-nodejs) 5 | - [¿Cómo funciona?](#cómo-funciona) 6 | - [Ventajas](#ventajas) 7 | - [Desventajas](#desventajas) 8 | - [Diferencias con entornos en navegador](#diferencias-con-entornos-en-navegador) 9 | - [Acceso al sistema](#acceso-al-sistema) 10 | - [Interpretación de los módulos](#interpretación-de-los-módulos) 11 | - [Estructura de un proyecto en Node.js](#estructura-de-un-proyecto-en-nodejs) 12 | - [package.json](#packagejson) 13 | - [Script de inicio](#script-de-inicio) 14 | - [package-lock.json](#package-lockjson) 15 | - [node\_modules](#node_modules) 16 | - [main.js](#mainjs) 17 | 18 | ## ¿Qué es Node.js? 19 | 20 | Nodejs se considera un entorno de ejecución, es decir nos permite, ejecutar JavaScript sin necesidad de estar en un 21 | navegador web. 22 | 23 | ## ¿Cómo funciona? 24 | 25 | Para lograr esto, se utilizo el motor V8 desarrollado por google y se embebió dentro de un programa en C++. 26 | 27 | ## Ventajas 28 | 29 | Node en un principio está pensado para no tener que usar hilos del procesador, gracias a su naturaleza asíncrona, usamos callbacks y llamadas para gestionar los procesos. Esto nos permite que un sólo hilo pueda atender a todas las peticiones del sistema, sin tener que esperar a una petición para atender a la siguiente. 30 | 31 | ## Desventajas 32 | 33 | Esto mismo es su principal problema, aunque node esta indicado para atender múltiples peticiones de forma simultánea, No esta indicado para procesos que demanden gran cantidad de CPU, cómo el renderizado de imágenes vídeos o audio en streaming. 34 | 35 | ## Diferencias con entornos en navegador 36 | 37 | A diferencia del uso de JavaScript en un navegador, aquí no tenemos acceso a recursos cómo window, o Document. 38 | 39 | ### Acceso al sistema 40 | 41 | Para acceder al sistema, desde node, tenemos módulos que podemos ir añadiendo a nuestro proyecto, de esta manera dotamos a Node.js de más utilizados, cómo la lectura de ficheros cómo los famosos JSON utilizando `fs` o acceso a la configuración del sistema usando `sys`. 42 | 43 | ### Interpretación de los módulos 44 | 45 | Al contrario que se indica en las últimas versiones de EMCScript, el uso de módulos en nuestras apps de Node.js, están de forma predeterminada en `Common-js` y aunque esto ultimo puede configurarse, puede llegar a dar problemas según el tipo de librería o framework que utilicemos, por lo tanto no recomiendo el uso de estos, si no has utilizado antes con node.js. 46 | 47 | ## Estructura de un proyecto en Node.js 48 | 49 | Un proyecto de Node.js tiene de base tres ficheros y una carpeta. 50 | 51 | Carpeta del proyecto 52 | ├── package.json 53 | ├── package-lock.json 54 | ├── main.js 55 | └── node_modules 56 | 57 | ## package.json 58 | 59 | Este fichero JSON de configuración es el que contiene la información del proyecto, cómo el nombre, la versión o los autores, también se definen los scripts de inicio y que dependencias serán de desarrollo y cuales no. 60 | 61 | Un ejemplo de este fichero sería el siguiente: 62 | 63 | ```json 64 | { 65 | "name": "nodejs", 66 | "version": "1.0.0", 67 | "description": "Proyecto de ejemplo de Node.js", 68 | "main": "index.js", 69 | "scripts": { 70 | "start": "node index.js", 71 | "dev": "nodemon index.js" 72 | }, 73 | "keywords": ["node", "nodejs", "javascript"], 74 | "author": "Altaskur", 75 | "license": "ISC", 76 | "dependencies": { 77 | "express": "^4.17.1" 78 | }, 79 | "devDependencies": { 80 | "nodemon": "^2.0.7" 81 | } 82 | } 83 | ``` 84 | 85 | ### Script de inicio 86 | 87 | Para ejecutar una aplicación de express, indicaremos dentro del apartado "scripts", la abreviatura que queramos usar para iniciar la aplicación, lo más común es usar ``start``, ``dev``, ``build`` o ``test``. 88 | 89 | - Start: Inicia la aplicación en modo producción. 90 | - Dev: Inicia la aplicación en modo desarrollo, en nuestro caso usando Nodemon. 91 | - Build: Utilizado para compilar el proyecto, en caso de que sea necesario, muy común en proyectos de Frontend. 92 | - Test: Utilizado para ejecutar los test de la aplicación. 93 | 94 | ejemplo del apartado de scripts: 95 | 96 | ```json 97 | "scripts": { 98 | "start": "node index.js", 99 | "dev": "nodemon index.js" 100 | }, 101 | ``` 102 | 103 | Ahora para ejecutar nuestra aplicación, solo tendremos que ejecutar `npm run` seguido del nombre definido en el apartado de scripts. 104 | 105 | ```bash 106 | npm run dev 107 | ``` 108 | 109 | > Ten en cuenta que con ``start`` no es necesario usar ``run`` simplemente con ``npm start`` será suficiente. 110 | 111 | ## package-lock.json 112 | 113 | Este fichero es generado automáticamente por npm, y contiene la información de las dependencias que se han instalado en el proyecto, así cómo las versiones de las mismas. Es recomendable no modificar este fichero, ya que puede dar problemas en la instalación de las dependencias. 114 | 115 | > Este fichero no debes subirlo a tu repositorio, ya que se genera automáticamente, añade el fichero a tu ``.gitignore`` para evitar subirlo a tu repositorio. 116 | 117 | ## node_modules 118 | 119 | Esta carpeta contiene todas las dependencias que se han descargado del proyecto, una vez ejecutamos ``npm install``, o su versión abreviada ``npm i``. 120 | 121 | > No debes subir esta carpeta a tu repositorio, ya que ocupa mucho espacio y con tan solo ejecutar ``npm i`` o ``npm install`` se descargaran todas las dependencias de nuevo. 122 | 123 | ## main.js 124 | 125 | Este es el fichero de inicio, del cual partirá nuestra aplicación, de normal se encuentra en la carpeta src y puedes encontrarlo también cómo index.js 126 | -------------------------------------------------------------------------------- /docs/eslint.md: -------------------------------------------------------------------------------- 1 | # Linters 2 | 3 | - [Linters](#linters) 4 | - [¿Qúe es un Linter?](#qúe-es-un-linter) 5 | - [¿Porqué es tan importante?](#porqué-es-tan-importante) 6 | - [EsLint](#eslint) 7 | - [Tipos de configuración](#tipos-de-configuración) 8 | - [Configuración con el asistente](#configuración-con-el-asistente) 9 | - [Pasos del asistente](#pasos-del-asistente) 10 | - [Configuración manual](#configuración-manual) 11 | 12 | ## ¿Qúe es un Linter? 13 | 14 | Un linter es una herramienta, que se encarga de examinar el 15 | código que generas, te ayuda a detectar errores de sintaxis, 16 | malas prácticas y facilitan el uso del las guías de estilo. 17 | 18 | ## ¿Porqué es tan importante? 19 | 20 | Con todo esto nos permite generar aplicaciones con un código de calidad, nos facilita el trabajo en equipo, ya que todos los implicados van a usar las mismas normas y nos evita de usar malos hábitos en el desarrollo. 21 | 22 | ## EsLint 23 | 24 | En este proyecto vamos a utilizar EsLint, es el linter más utilizado de node.js, es muy configurable, tiene bastante soporte por la comunidad así cómo una excelente documentación. 25 | 26 | ## Tipos de configuración 27 | 28 | Podemos configurar EsLint de dos formas, la primera forma es a traves del asistente cli, que nos ayudará con unos sencillos pasos a crear el archivo de configuración, la segunda forma es crear el archivo de configuración a mano. Este archivo se llama ``.eslintrc`` y podemos guardarlo en distintos formatos, cómo archivo ``.js``, ``.json`` o ``.yaml``. En este taller vamos a utilizar el formato ``.JSON``, ya que es el más sencillo de entender. 29 | 30 | Estas dos formas se pueden y suelen combinarse, es decir, podemos crear el archivo de configuración con el asistente y luego modificarlo a mano. Estos cambios se pueden hacer en cualquier momento, ya que EsLint se ejecuta en tiempo de ejecución. 31 | 32 | ## Configuración con el asistente 33 | 34 | Para este taller vamos a configurar EsLint con el asistente, ya que fácilmente nos crea el archivo de configuración y nos ayuda a instalar las dependencias necesarias. Una vez tenemos el archivo podemos modificarlo a mano. 35 | 36 | Para configurar EsLint, vamos a usar el comando: 37 | 38 | ```bash 39 | npm init @eslint/config 40 | ``` 41 | 42 | ### Pasos del asistente 43 | 44 | Ahora veremos los distintos pasos que da asistente, para configurar EsLint (a la hora de escribir este taller, la versión de EsLint es la ^8.47.0): 45 | 46 | Si no tenemos EsLint instalado el asistente, nos preguntará si queremos instalarlo:: 47 | 48 | > Need to install the following packages: @eslint/create-config@0.4.6 Ok to proceed? (y) **Yes** 49 | 50 | En este paso nos pregunta para que queremos usar EsLint, tenemos tres opciones: 51 | 52 | - **To find problems only**: Esta opción nos permite encontrar problemas. 53 | - **To check syntax only**: Esta opción nos permite comprobar la sintaxis. 54 | - **To check syntax, find problems, and enforce code style**: Esta opción es la que vamos a usar en este taller, nos permite comprobar la sintaxis, encontrar problemas y aplicar un estilo de código. 55 | 56 | > How would you like to use EsLint? **To check syntax, find problems, and enforce code style** 57 | 58 | En este paso nos pregunta que tipo de módulos vamos a usar, si CommonJS o módulos ES6: 59 | 60 | > ? What type of modules does your project use? ... **CommonJS (require/exports)** 61 | 62 | Cómo EsLint podemos usarlo tanto en Frontend cómo en Backend, nos pregunta si vamos a usar algún framework de frontend, este taller es de backend, así que seleccionamos la opción **None of these** 63 | 64 | > ? Which framework does your project use? ... **None of these** 65 | 66 | También EsLint puede trabajar con TypeScript, en este taller no vamos a usar TypeScript, así que seleccionamos la opción **No** 67 | 68 | - ? Does your project use TypeScript? **No** 69 | 70 | En este paso, nos pregunta dónde se va ejecutar nuestro proyecto, si en el navegador o en Node, en este taller vamos a usar Node, ya que es un proyecto del lado de servidor así que seleccionamos la opción **Node** 71 | 72 | > ? Where does your code run? **Node** 73 | 74 | EsLint nos permite usar gran cantidad de guías de estilo, si no sabemos cual, puede intentar adivinarla e incluso crear nuestra guía de estilo personalizada, en este taller vamos a usar una guía de estilo popular, en concreto la de Airbnb, ya que es la más cercana al estándar de JavaScript. 75 | 76 | > ? How would you like to define a style for your project? ... **Use a popular style guide** 77 | 78 | En este paso nos da la opción de elegir la guía de estilo, recuerda que en este taller vamos a usar la guía de estilo de Airbnb. 79 | 80 | > ? Which style guide do you want to follow? ... **Airbnb: ** 81 | 82 | Ahora el asistente nos pregunta en que formato de archivo queremos guardar la configuración, cómo he indicado anteriormente, en este taller vamos a usar el formato ``.JSON`` 83 | 84 | > ? What format do you want your config file to be in? ... **JSON** 85 | 86 | En este paso nos pregunta si queremos instalar ahora las dependencias necesarias, seleccionamos la opción **Yes** 87 | 88 | > ? Would you like to install them now? ... **Yes** 89 | 90 | Y por último nos pregunta que gestor de paquetes queremos usar, en nuestro caso vamos a usar **npm** 91 | 92 | - ? Which package manager do you want to use? ... **npm** 93 | 94 | Si todo ha ido bien, tendremos un archivo ``.eslintrc.json`` en la raíz de nuestro proyecto, con la configuración que hemos seleccionado. [ver archivo .eslintrc.json](../.eslintrc.json) 95 | 96 | ## Configuración manual 97 | 98 | Ahora veremos cómo es la estructura del archivo de configuración de EsLint: 99 | 100 | ```json 101 | { 102 | "env": { 103 | "browser": true, 104 | "commonjs": true, 105 | "es2021": true 106 | }, 107 | "extends": "airbnb-base", 108 | "parserOptions": { 109 | "ecmaVersion": "latest" 110 | }, 111 | "rules": { 112 | } 113 | } 114 | ```` 115 | 116 | Cómo puedes ver, en el archivo, podemos encontrar tres partes principales: 117 | 118 | - **env**: En esta parte podemos indicar en que entorno se va a ejecutar nuestro código, por ejemplo, si vamos a usar el navegador, Node, etc. 119 | - **extends**: En esta parte podemos indicar que guía de estilo vamos a usar, en este taller vamos a usar la guía de estilo de Airbnb. 120 | - **rules**: En esta parte podemos indicar las reglas que queremos aplicar, por ejemplo, si queremos usar punto y coma, comillas simples o dobles, etc. 121 | 122 | Un ejemplo de modificación que suelo aplicar en los proyectos, es que EsLint no marque cómo alerta 123 | el uso de ``console.log``, ya que durante el desarrollo es muy útil para depurar el código, pero en producción no debería estar, ya que puede ser un problema de seguridad. 124 | 125 | Para ello, en la parte de ``rules`` añadimos la sentencia ``"no-console": "off"`` 126 | 127 | ```json 128 | "rules": { 129 | "no-console": "off" 130 | } 131 | ``` 132 | -------------------------------------------------------------------------------- /docs/jsonWebToken.md: -------------------------------------------------------------------------------- 1 | # JSON Web Token 2 | 3 | - [JSON Web Token](#json-web-token) 4 | - [Usos](#usos) 5 | - [Estructura](#estructura) 6 | - [Header](#header) 7 | - [Payload](#payload) 8 | - [Signature](#signature) 9 | - [Uso en el taller](#uso-en-el-taller) 10 | - [¿Cómo funciona de jsonwebtoken?](#cómo-funciona-de-jsonwebtoken) 11 | - [Tiempo de expiración](#tiempo-de-expiración) 12 | - [Generar clave secreta](#generar-clave-secreta) 13 | - [Explicación del script](#explicación-del-script) 14 | - [Instalación](#instalación) 15 | - [.env](#env) 16 | - [Creación de un JWT](#creación-de-un-jwt) 17 | - [Verificación de un JWT](#verificación-de-un-jwt) 18 | 19 | JSON Web Token o JWT es un estándar abierto [rfc7519](https://tools.ietf.org/html/rfc7519), que se define cómo una forma de pasar información entre dos partes. La información que se transmite es un objeto JSON que está firmado digitalmente y se verifica la integridad de la información que contiene. 20 | 21 | ## Usos 22 | 23 | El uso más común de JWT es cómo autenticación e intercambio de información entre dos partes, por ejemplo, un cliente y un servidor. El cliente envía un JWT al servidor y el servidor verifica la integridad del token y la información que contiene. 24 | 25 | Dentro de la información que se transmite en el JWT se puede incluir información de usuario, permisos, roles, etc. El servidor puede utilizar esta información para validar que el usuario tiene los permisos necesarios para acceder a los recursos que está solicitando. 26 | 27 | ## Estructura 28 | 29 | Un JWT se compone de tres partes: 30 | 31 | - Header 32 | - Payload 33 | - Signature 34 | 35 | ### Header 36 | 37 | El header contiene dos partes, el tipo de token y el algoritmo de encriptación que se utilizó para firmar el token. 38 | 39 | ```json 40 | { 41 | "alg": "HS256", 42 | "typ": "JWT" 43 | } 44 | ``` 45 | 46 | ### Payload 47 | 48 | El payload contiene la información que se quiere transmitir, puede ser cualquier información que se desee, pero se recomienda que sea información que pueda ser verificada por el servidor. 49 | 50 | ```json 51 | { 52 | "sub": "1234567890", 53 | "name": "John Doe", 54 | "admin": true 55 | } 56 | ``` 57 | 58 | ### Signature 59 | 60 | La firma se crea a partir de la codificación en base64 del header y el payload, y se firma con el algoritmo especificado en el header. 61 | 62 | ```js 63 | HMACSHA256( 64 | base64UrlEncode(header) + "." + 65 | base64UrlEncode(payload), 66 | secret 67 | ) 68 | ``` 69 | 70 | ## Uso en el taller 71 | 72 | En este taller vamos a utilizar la librería [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) para crear y verificar los JWT. Vamos a guardar a nuestro usuario, y en servidor vamos a verificar que el usuario tenga los permisos necesarios para acceder a los recursos que está solicitando. 73 | 74 | Vamos a necesitar guardar en nuestro .env la clave secreta que vamos a utilizar para firmar los JWT. 75 | 76 | ## ¿Cómo funciona de jsonwebtoken? 77 | 78 | El uso de la librería jsonwebtoken es muy sencillo, solo necesitamos importarla y usar el método `sign` para crear un JWT y el método `verify` para verificar la integridad del token. 79 | 80 | A la hora de "firmar" el token, necesitamos indicarle la información, en este caso el usuario, la clave secreta guardada en el .env y el tiempo de expiración del token. 81 | 82 | cabe indicar que existe dentro de JWT el tipo bearer, que es el que se usa para autenticar usuarios dentro del token, para hacerlo tan solo tenemos que añadir bearer al principio del token separado por un espacio. 83 | 84 | > Recuerda eliminar el bearer del token antes de verificarlo. 85 | 86 | ### Tiempo de expiración 87 | 88 | El tiempo de expiración es un número en segundos indicando cuánto tiempo tiene de validez el token, en el momento que el token expire el servidor no lo va a aceptar. 89 | 90 | Se recomienda que el tiempo de expiración sea lo más corto posible, pero que sea suficiente para que el usuario pueda realizar las acciones que necesita. De normal se utiliza un tiempo de expiración de 15 minutos a 1 hora. 91 | 92 | ## Generar clave secreta 93 | 94 | es recomendable usar una clave secreta de al menos 36 caracteres, para generar una clave secreta con validez criptográfica, te dejo aquí un pequeño script que puedes ejecutar en tu terminal para generar una clave secreta. 95 | 96 | ```bash 97 | node -e "console.log(require('crypto').randomBytes(36).toString('hex'));" 98 | ``` 99 | 100 | ### Explicación del script 101 | 102 | En el script anterior ejecutamos NodeJS con una expresión sencilla, para ello usamos el parámetro `e` 103 | para indicarle a node que no ejecute un archivo, sino que ejecute una expresión. 104 | 105 | En la expresión escrita con entre "", usamos el módulo `crypto` de NodeJS para generar una clave secreta de 36 bytes, y luego la convertimos a hexadecimal para que sea más fácil de leer. 106 | 107 | Al usar `console.log` estamos imprimiendo la clave secreta en la terminal. 108 | 109 | ## Instalación 110 | 111 | ```bash 112 | npm install jsonwebtoken 113 | ``` 114 | 115 | ## .env 116 | 117 | Ahora vamos añadir la clave secreta a nuestro .env 118 | 119 | ```env 120 | JWT_SECRET=1484ff743a0dd6f4abfe9cb 121 | ``` 122 | 123 | No te preocupes, esta clave secreta no es válida, es solo un ejemplo. 124 | 125 | ## Creación de un JWT 126 | 127 | Para crear un JWT se utiliza el método `sign` de la librería `jsonwebtoken`. Este método recibe dos parámetros, el payload y el secret. Cómo hemos visto en la estructura, en el payload colocamos la información de nuestro usuario. El secret es una clave secreta que solo debe conocer el servidor. 128 | 129 | Por último vamos añadirle un tiempo de expiración al token. Para ello vamos a añadir un tercer parámetro al método `sign` con el tiempo de expiración en segundos. 130 | 131 | ```js 132 | const jwt = require('jsonwebtoken'); 133 | const secret = process.env.JWT_SECRET; 134 | 135 | const payload = { 136 | sub: '1234567890', 137 | name: 'John Doe', 138 | admin: true 139 | }; 140 | 141 | const expiresIn = 60; 142 | 143 | const token = jwt.sign(payload, secret, { expiresIn }); 144 | 145 | const bearerToken = `bearer ${token}`; 146 | 147 | ``` 148 | 149 | ## Verificación de un JWT 150 | 151 | Para verificar un JWT se utiliza el método `verify` de la librería `jsonwebtoken`. Este método recibe dos parámetros, el token y el secret. Nos va a devolver el payload del token si la verificación es correcta, o un error si la verificación falla. 152 | 153 | ```js 154 | const jwt = require('jsonwebtoken'); 155 | 156 | const verificarToken = (token) => { 157 | 158 | const parsedToken = token.split(' ')[1]; 159 | const secret = process.env.JWT_SECRET; 160 | try { 161 | const payload = jwt.verify(token, secret); 162 | return payload; 163 | } catch (error) { 164 | console.log(error); 165 | return false; 166 | } 167 | }; 168 | 169 | ``` 170 | 171 | Aunque el estándar y la teoria es muy amplia, la librearía jsonwebtoken nos facilita mucho la creación y verificación de los JWT, en apenas unas lineas. 172 | -------------------------------------------------------------------------------- /docs/ejemplos/express.md: -------------------------------------------------------------------------------- 1 | # Ejemplo de aplicaciones con Express 2 | 3 | ## Ejemplo de una aplicación de Express 4 | 5 | ```js 6 | const express = require('express'); 7 | const app = express(); 8 | 9 | app.use(express.json()); 10 | 11 | app.get('/', (req, res) => { 12 | res.status(200).send('Hello World'); 13 | }); 14 | 15 | app.set('port', 3000); 16 | 17 | app.listen(app.get('port'), () => { 18 | console.log(`Server on port ${app.get('port')}`); 19 | }); 20 | ``` 21 | 22 | ## Ejemplo de Curd con Express 23 | 24 | ```js 25 | const express = require('express'); 26 | const app = express(); 27 | const bd = require('./db'); 28 | 29 | app.use(express.json()); 30 | 31 | app.get('/', (req, res) => { 32 | res.status(200).send('Hello World'); 33 | }); 34 | 35 | app.get('/users',async (req, res) => { 36 | try{ 37 | const users = await bd.getUsers(); 38 | res.status(200).json(users); 39 | } catch(err){ 40 | res.status(500).json({'error': 'Error al obtener los usuarios'}); 41 | } 42 | const users = bd.getUsers(); 43 | res.status(200).json(users); 44 | }); 45 | 46 | app.get('/users/:id', async (req, res) => { 47 | try{ 48 | const user = await bd.getUser(req.params.id); 49 | res.status(200).json(user); 50 | }catch(err){ 51 | res.status(404).json({'error': 'User not found'}); 52 | } 53 | }); 54 | 55 | app.post('/users', async (req, res) => { 56 | try{ 57 | const user = await bd.createUser(req.body); 58 | res.status(200).json(user); 59 | }catch(err){ 60 | res.status(400).json({'error': 'Error al crear el usuario'}); 61 | } 62 | 63 | }); 64 | 65 | app.put('/users/:id', async (req, res) => { 66 | try{ 67 | const user = await bd.updateUser(req.params.id, req.body); 68 | res.status(200).json(user); 69 | }catch(err){ 70 | res.status(400).json({'error': 'Error al actualizar el usuario'}); 71 | } 72 | }); 73 | 74 | app.delete('/users/:id', async (req, res) => { 75 | try{ 76 | await bd.deleteUser(req.params.id); 77 | res.status(200).send(`User ${req.params.id} Deleted`); 78 | }catch(err){ 79 | res.status(400).json({'error': 'Error al eliminar el usuario'}); 80 | } 81 | }); 82 | 83 | app.set('port', 3000); 84 | 85 | app.listen(app.get('port'), () => { 86 | console.log(`Server on port ${app.get('port')}`); 87 | }); 88 | ``` 89 | 90 | ## Ejemplo del uso de Router 91 | 92 | ```js 93 | // router/users.js 94 | const express = require('express'); 95 | const router = express.Router(); 96 | 97 | router.get('/', (req, res) => { 98 | res.status(200).send('Hello World'); 99 | }); 100 | ``` 101 | 102 | ```js 103 | // main.js 104 | const express = require('express'); 105 | const app = express(); 106 | const usersRouter = require('./router/users'); 107 | 108 | app.use(express.json()); 109 | 110 | app.use('/users', usersRouter); 111 | 112 | app.set('port', 3000); 113 | 114 | app.listen(app.get('port'), () => { 115 | console.log(`Server on port ${app.get('port')}`); 116 | }); 117 | ``` 118 | 119 | ## Ejemplo de un Crud con Router y Controller 120 | 121 | ```js 122 | // db.js 123 | const pgp = require('pg-promise')(); 124 | require('dotenv').config(); 125 | 126 | const { env } = process; 127 | let dbInstance = null; 128 | 129 | const connectionString = `postgres://${env.DB_USER}:${env.DB_PASSWORD}@${env.DB_HOST}:${env.DB_PORT}/${env.DB_DATABASE}?ssl=true&sslrootcert=${env.DB_CERT}`; 130 | 131 | const getDatabaseInstance = () => { 132 | if (!dbInstance) { 133 | dbInstance = pgp(connectionString); 134 | } 135 | return dbInstance; 136 | }; 137 | 138 | module.exports = { 139 | db: getDatabaseInstance(), 140 | }; 141 | ``` 142 | 143 | ```js 144 | // .env 145 | DB_USER=postgres 146 | DB_PASSWORD=postgres 147 | DB_HOST=localhost 148 | DB_PORT=5432 149 | DB_DATABASE=express 150 | DB_CERT=certs/ca-certificate.crt 151 | ``` 152 | 153 | ```js 154 | //db/users.js 155 | const { db } = require('./index'); 156 | 157 | const getUsers = () => db.any('SELECT * FROM users'); 158 | 159 | const getUser = (id) => db.one('SELECT * FROM users WHERE id = $1', id); 160 | 161 | const createUser = (user) => db.one('INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *', [user.name, user.email]); 162 | 163 | const updateUser = (id, user) => db.one('UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *', [user.name, user.email, id]); 164 | 165 | const deleteUser = (id) => db.none('DELETE FROM users WHERE id = $1', id); 166 | 167 | module.exports = { 168 | getUsers, 169 | getUser, 170 | createUser, 171 | updateUser, 172 | deleteUser 173 | }; 174 | ``` 175 | 176 | ```js 177 | // controllers/users.js 178 | const bd = require('../db'); 179 | const bdUsers = require('../db/users'); 180 | 181 | const getUsers = async (req, res) => { 182 | try{ 183 | const users = await bdUsers.getUsers(); 184 | res.status(200).json(users); 185 | } catch(err){ 186 | res.status(500).json({'error': 'Error al obtener los usuarios'}); 187 | } 188 | }; 189 | 190 | const getUser = async (req, res) => { 191 | try{ 192 | const user = await bdUsers.getUser(req.params.id); 193 | res.status(200).json(user); 194 | }catch(err){ 195 | res.status(404).json({'error': 'User not found'}); 196 | } 197 | }; 198 | 199 | const createUser = async (req, res) => { 200 | try{ 201 | const user = await bdUsers.createUser(req.body); 202 | res.status(200).json(user); 203 | }catch(err){ 204 | res.status(400).json({'error': 'Error al crear el usuario'}); 205 | } 206 | 207 | }; 208 | 209 | const updateUser = async (req, res) => { 210 | try{ 211 | const user = await bdUsers.updateUser(req.params.id, req.body); 212 | res.status(200).json(user); 213 | }catch(err){ 214 | res.status(400).json({'error': 'Error al actualizar el usuario'}); 215 | } 216 | }; 217 | 218 | const deleteUser = async (req, res) => { 219 | try{ 220 | await bdUsers.deleteUser(req.params.id); 221 | res.status(200).send(`User ${req.params.id} Deleted`); 222 | }catch(err){ 223 | res.status(400).json({'error': 'Error al eliminar el usuario'}); 224 | } 225 | }; 226 | 227 | module.exports = { 228 | getUsers, 229 | getUser, 230 | createUser, 231 | updateUser, 232 | deleteUser 233 | }; 234 | ``` 235 | 236 | ```js 237 | // main.js 238 | const express = require('express'); 239 | const app = express(); 240 | const usersRouter = require('./router/users'); 241 | 242 | app.use(express.json()); 243 | 244 | app.use('/users', usersRouter); 245 | 246 | app.set('port', 3000); 247 | 248 | app.listen(app.get('port'), () => { 249 | console.log(`Server on port ${app.get('port')}`); 250 | }); 251 | ``` 252 | 253 | ```js 254 | // router/users.js 255 | const express = require('express'); 256 | const router = express.Router(); 257 | const usersController = require('../controllers/users'); 258 | 259 | router.get('/', usersController.getUsers); 260 | router.get('/:id', usersController.getUser()); 261 | router.post('/', usersController.createUser); 262 | router.put('/:id', usersController.updateUser); 263 | router.delete('/:id', usersController.deleteUser); 264 | 265 | module.exports = router; 266 | ``` 267 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Repositorio para los talleres de Donweb 3 | 4 | Un repositorio para seguir el taller de NodeJS y Express para Donweb 5 | 6 | Aquí veremos cómo montar una API-REST con NodeJS y Express, utilizando una base de datos en MongoDB y autenticación con JWT. 7 | 8 | El taller esta dividido en dos partes: 9 | 10 | La primera parte es la creación de nuestro proyecto veremos la funciones básicas de express, aprenderemos a usar Eslint, atenderemos a las peticiones HTTP, y por ultimo crearemos las rutas de nuestra aplicación. 11 | 12 | La segunda parte, nos conectaremos a nuestra base de datos de MongoDB, 13 | crearemos los modelos y las consultas a la base de datos, y por último nos autenticaremos con JWT. 14 | 15 | - [Repositorio para los talleres de Donweb](#repositorio-para-los-talleres-de-donweb) 16 | - [Primera Parte: Creación del proyecto](#primera-parte-creación-del-proyecto) 17 | - [Listado de dependencias](#listado-de-dependencias) 18 | - [Dependencias de producción](#dependencias-de-producción) 19 | - [Dependencias de desarrollo](#dependencias-de-desarrollo) 20 | - [Configuración de Eslint](#configuración-de-eslint) 21 | - [.env](#env) 22 | - [Estructura de carpetas](#estructura-de-carpetas) 23 | - [Archivo main.js](#archivo-mainjs) 24 | - [Autores](#autores) 25 | 26 | ## Primera Parte: Creación del proyecto 27 | 28 | Vamos a crear nuestro primer proyecto con NodeJS, si nunca has trabajado con NodeJS te recomiendo mirar 29 | el apartado [docs/Express](./docs/express.md) para que te familiarices un poco con la herramienta. 30 | 31 | Es importante tener en cuenta que NodeJs, va a tomar el directorio actual, cómo el directorio raíz de nuestro proyecto, por lo que es importante que creemos una carpeta nueva para nuestro proyecto. 32 | 33 | En nuestro caso partiremos de donweb-taller-express, por lo que crearemos una carpeta con ese nombre. 34 | 35 | Recuerda tener instalado NodeJs, antes de continuar. 36 | 37 | una vez dentro del proyecto inicializamos un nuevo proyecto de NodeJS, para ello abrimos la consola dentro del directorio del proyecto ``donweb-taller-express`` y ejecutamos el siguiente comando: 38 | 39 | ```bash 40 | npm init 41 | ``` 42 | 43 | Se abrirá un asistente, con unos pasos a seguir, si quieres más detalles sobre este asistente puedes mirar el apartado [docs/nodeJs](./docs/nodeJs.md) 44 | 45 | No te preocupes, los pasos son muy sencillos y en el caso de tener duda, siempre puedes dejar el valor por defecto. 46 | 47 | Una vez terminado, encontraremos un nuevo archivo en nuestro proyecto llamado ``package.json`` 48 | este archivo contiene la información de nuestro proyecto. 49 | 50 | ### Listado de dependencias 51 | 52 | Las dependencias en NodeJs, son las librerías que vamos a utilizar, una librería no es más que un conjunto de funciones que nos ayudan a realizar una tarea en concreto. Por ejemplo, la librería express, nos ayuda a atender peticiones HTTP. 53 | 54 | Existen dos tipos de dependencias, las dependencias de producción y las dependencias de desarrollo. 55 | 56 | #### Dependencias de producción 57 | 58 | Para este taller vamos a utilizar las siguientes dependencias de producción: 59 | 60 | - [bcrypt](./docs/bcrypt.md) 61 | - [cors](./docs/cors.md) 62 | - [express](./docs/express.md) 63 | - [dotEnv](./docs/dotEnv.md) 64 | - [jsonwebtoken](./docs/jsonWebToken.md) 65 | - [mongoose](./docs/mongoose.md) 66 | - [express-validator](https://www.npmjs.com/package/express-validator) 67 | 68 | Para instalarlas ejecutamos el siguiente comando: 69 | 70 | ```bash 71 | npm i bcrypt cors express dotenv jsonwebtoken mongoose express-validator 72 | ``` 73 | 74 | #### Dependencias de desarrollo 75 | 76 | Las dependencias de desarrollo, son las librerías que nos ayudan a desarrollar nuestro proyecto, por ejemplo, nodemon, nos ayuda a reiniciar el servidor cada vez que guardamos un cambio en nuestro código. 77 | 78 | Para este taller vamos a utilizar las siguientes dependencias de desarrollo: 79 | 80 | - [eslint](./docs/eslint.md) 81 | - [nodemon](./docs/nodemon.md) 82 | - [morgan](./docs/morgan.md) 83 | 84 | En algunos proyectos, morgan también se utiliza en producción, pero en nuestro caso sólo vamos a utilizarlo para controlar los logs en desarrollo. 85 | 86 | Para instalarlas ejecutamos el siguiente comando: 87 | 88 | ```bash 89 | npm i -D eslint nodemon morgan 90 | ``` 91 | 92 | En el apartado `docs` te dejo una pequeña descripción de cada una de las dependencias. Pero también puedes consultar los enlaces de npm que te deje en cada una de ellas. 93 | 94 | ### Configuración de Eslint 95 | 96 | El siguiente paso es configurar nuestro linter, en este caso vamos a utilizar Eslint, cómo te ido comentado a lo largo del taller, tienes más información en el apartado [docs/eslint](./docs/eslint.md) 97 | 98 | Para configurar Eslint, vamos a usar el comando: 99 | 100 | ```bash 101 | npm init @eslint/config 102 | ``` 103 | 104 | Al igual que con el comando `npm init`, se abrirá un asistente, con unos pasos a seguir, si quieres más detalles sobre este asistente, o tienes alguna duda puedes mirar el apartado [docs/eslint](./docs/eslint.md) 105 | 106 | Los valores que vamos a utilizar son los siguientes: 107 | 108 | - Need to install the following packages: @eslint/create-config@0.4.6 Ok to proceed? (y) **Yes** 109 | - ? How would you like to use ESLint? ... **To check syntax, find problems, and enforce code style** 110 | - ? What type of modules does your project use? ... **CommonJS (require/exports)** 111 | - ? Which framework does your project use? ... **None of these** 112 | - ? Does your project use TypeScript? **No** 113 | - ? Where does your code run? **Node** 114 | - ? How would you like to define a style for your project? ... **Use a popular style guide** 115 | - ? Which style guide do you want to follow? ... **Airbnb: ** 116 | - ? What format do you want your config file to be in? ... **JSON** 117 | - ? Would you like to install them now? ... **Yes** 118 | - ? Which package manager do you want to use? ... **npm** 119 | 120 | No te preocupes si te has equivocado en algún paso, siempre puedes volver a ejecutar el comando y volver a configurar Eslint. Si usas windows, puedes cancelar el asistente con `Ctrl + C` 121 | 122 | Una vez terminado, dispondremos de un nuevo archivo `.eslintrc.json`, en el que se encuentra la configuración de Eslint. 123 | 124 | ## .env 125 | 126 | El siguiente paso es crear nuestro archivo `.env`, en el que vamos a guardar las variables de entorno de nuestro proyecto. aquí vamos a guardar las credenciales de acceso a la base de datos, el puerto en el que va a correr nuestro servidor y el secreto que vamos a utilizar para firmar los tokens. 127 | 128 | ¿Porqué no vamos a guardar las credenciales de acceso a la base de datos en el código? 129 | Porque si subimos nuestro código a un repositorio público, cualquiera podría ver nuestras credenciales y acceder a nuestra base de datos, de esta manera sólo nosotros vamos a tener acceso a ellas y además podemos tenerlas disponibles en cualquier parte de nuestro código. 130 | 131 | ahora simplemente creamos un archivo llamado `.env` en la raíz de nuestro proyecto y añadimos las siguientes variables: 132 | 133 | ```env 134 | APP_PORT=3001 135 | ``` 136 | 137 | Más adelante añadiremos más variables, pero por ahora con esta es suficiente. 138 | 139 | ## Estructura de carpetas 140 | 141 | Ahora sólo nos queda crear la estructura de carpetas para nuestro proyecto, aquí solo te voy a mostrar la estructura de carpetas que vamos a utilizar, pero si quieres más información sobre la estructura de carpetas, puedes mirar el apartado [docs/express.md](./docs/express.md) 142 | 143 | ```bash 144 | donweb-taller-express 145 | ├── .env 146 | ├── .eslintrc.json 147 | ├── package.json 148 | ├── package-lock.json 149 | ├── node_modules 150 | ├── README.md 151 | ├── src 152 | │ ├── app.js 153 | │ │ └── app.js 154 | │ ├── controllers 155 | │ │ └── users.js 156 | │ ├── routes 157 | │ │ └── users.js 158 | │ └── main.js 159 | └── .gitignore 160 | ``` 161 | 162 | ## Archivo main.js 163 | 164 | El archivo `main.js` es el punto de entrada de nuestro proyecto, aquí vamos a importar el archivo `app.js` y vamos a ejecutar el servidor. 165 | 166 | Es recomendado separar la configuración del servidor del punto de entrada, ya que permite una mejor escalabilidad del proyecto, nos permite tener una mejor organización del código y nos permite realizar pruebas unitarias de una manera más sencilla. 167 | 168 | - [ver archivo main.js](./src/main.js) 169 | - [ver archivo app.js](./src/app.js) 170 | 171 | ## Autores 172 | 173 | - [@altaskur](https://github.com/altaskur) 174 | -------------------------------------------------------------------------------- /docs/mongoose.md: -------------------------------------------------------------------------------- 1 | # Mongoose 2 | 3 | - [Mongoose](#mongoose) 4 | - [Desventajas](#desventajas) 5 | - [Bases de datos relacionales vs no relacionales](#bases-de-datos-relacionales-vs-no-relacionales) 6 | - [Bases de datos](#bases-de-datos) 7 | - [Bases de datos relacionales](#bases-de-datos-relacionales) 8 | - [Ventajas de las bases de datos relacionales](#ventajas-de-las-bases-de-datos-relacionales) 9 | - [Desventajas de las bases de datos relacionales](#desventajas-de-las-bases-de-datos-relacionales) 10 | - [Bases de datos no relacionales](#bases-de-datos-no-relacionales) 11 | - [Ventajas de las bases de datos no relacionales](#ventajas-de-las-bases-de-datos-no-relacionales) 12 | - [Desventajas de las bases de datos no relacionales](#desventajas-de-las-bases-de-datos-no-relacionales) 13 | - [Instalación de Mongoose](#instalación-de-mongoose) 14 | - [Uso de Mongoose](#uso-de-mongoose) 15 | - [.env](#env) 16 | - [Conexión a la base de datos](#conexión-a-la-base-de-datos) 17 | - [Esquema de la URL de conexión](#esquema-de-la-url-de-conexión) 18 | - [Esquema del objeto de configuración](#esquema-del-objeto-de-configuración) 19 | - [Método connect](#método-connect) 20 | - [Model](#model) 21 | - [Añadir al documento](#añadir-al-documento) 22 | - [Consultar entradas del documento](#consultar-entradas-del-documento) 23 | - [Actualizar entrada de un documento](#actualizar-entrada-de-un-documento) 24 | - [Eliminar un documento](#eliminar-un-documento) 25 | 26 | Mongoose es una librería para Node.js que nos permite trabajar con bases de datos MongoDB, nos añade una capa de funcionalidades que nos permite el trabajo y el entendimiento de la base de datos. 27 | 28 | Recomiendo el uso de mongoose, porque añade validaciones, tipos de datos, gestion de conexión e incluso nos permite crear relaciones entre colecciones. 29 | 30 | ## Desventajas 31 | 32 | Aunque Mongoose nos aporta muchas ventajas, también tiene sus desventajas, principalmente es más lento que trabajar directamente con la base de datos al crear un modelo intermedio, además las relaciones entre colecciones no son tan eficientes cómo en una base de datos relacional. 33 | 34 | Aún así, el equilibrio entre ventajas y desventajas es muy favorable para el uso de Mongoose. 35 | 36 | ## Bases de datos relacionales vs no relacionales 37 | 38 | ### Bases de datos 39 | 40 | Una base de datos es un sistema de almacenamiento de datos, muy optimizado que nos garantiza la persistencia, integridad y seguridad de los datos. 41 | 42 | ### Bases de datos relacionales 43 | 44 | Una base de datos relacional se estructura en conjuntos llamados tablas que en su interior tienen filas y columnas, las filas son los registros y las columnas son los campos, cada registro tiene un identificador único llamado clave primaria. 45 | 46 | > Imagínate una tabla de una hoja de cálculo, cada fila es un registro y cada columna es un campo. 47 | 48 | Ahora bien cada tabla puede tener una relación con otra tabla, por ejemplo imagina que una base de datos de un blog, tendríamos dos tablas 49 | una para los usuarios y otra para las entradas, por lo tanto cada entrada del blog estaría relacionada con su autor, en este caso el usuario y cada usuario estaría relacionado con todas sus entradas. 50 | 51 | #### Ventajas de las bases de datos relacionales 52 | 53 | - Son muy eficientes para realizar consultas complejas. 54 | - Son muy eficientes para realizar consultas que involucren varias tablas. 55 | 56 | #### Desventajas de las bases de datos relacionales 57 | 58 | - El esquema de datos es rígido, por lo tanto, las entradas no pueden tener distintos campos. 59 | - Si la estructura de datos no está bien definida, la complejidad de las consultas aumenta. 60 | 61 | ### Bases de datos no relacionales 62 | 63 | Las bases de datos no relacionales, estructuran los datos en colecciones, cada colección tiene documentos, que no son más que objetos JSON. 64 | 65 | > Imagínate una colección cómo un libro, formado por documentos, que son las páginas del libro. 66 | 67 | Ahora bien, cada documento puede tener una estructura distinta, por lo tanto, no hay un esquema de datos definido, esto nos permite tener una gran flexibilidad a la hora de almacenar datos. 68 | 69 | ### Ventajas de las bases de datos no relacionales 70 | 71 | - Són muy rápidas para realizar consultas que involucren una sola colección. 72 | - No tienen un esquema de datos definido, por lo tanto, son perfectas para almacenar datos con estructuras distintas. 73 | - Son muy eficientes para almacenar grandes cantidades de datos. 74 | - Permiten escalar horizontalmente, es decir, añadir más servidores para aumentar la capacidad de almacenamiento. 75 | 76 | ### Desventajas de las bases de datos no relacionales 77 | 78 | - No son muy eficientes para realizar consultas que involucren varias colecciones. 79 | - Al no tener un esquema de datos definido, las consultas complejas son más difíciles de realizar. 80 | 81 | ## Instalación de Mongoose 82 | 83 | Para instalar mongoose, ejecutamos el siguiente comando: 84 | 85 | ```bash 86 | npm install mongoose 87 | ``` 88 | 89 | ## Uso de Mongoose 90 | 91 | ### .env 92 | 93 | En nuestro .env del proyecto vamos añadir la url de la conexión a nuestro servidor de MongoDB alojado en [Donweb]( https://donweb.com/hosting-cloud-servers-vps?utm_source=altaskur-twitch&utm_campaign=embajadores-de-marca&utm_medium=text-link&utm_content=stream-twitch) 94 | 95 | ```env 96 | MONGO_URL=mongodb://test:test@localhost:27017/blog 97 | ``` 98 | 99 | ### Conexión a la base de datos 100 | 101 | Para conectarnos a la base de datos, usaremos el método `connect` de mongoose, dónde le paremos la url del servidor de MongoDB, alojado en [Donweb]( https://donweb.com/hosting-cloud-servers-vps?utm_source=altaskur-twitch&utm_campaign=embajadores-de-marca&utm_medium=text-link&utm_content=stream-twitch) 102 | 103 | El método connect devuelve una promesa, por lo tanto, podemos usar async/await para manejar la conexión. el método espera: 104 | 105 | - La url del servidor de MongoDB. 106 | - Un objeto de configuración, en este caso, le pasamos el parámetro `useNewUrlParser` con el valor `true`, para que use el nuevo analizador de cadenas de conexión. 107 | - useUnifiedTopology: true, para que use el nuevo motor de topología unificada. 108 | - dbName: El nombre de la base de datos. 109 | 110 | #### Esquema de la URL de conexión 111 | 112 | ```url 113 | mongodb://:@: 114 | ``` 115 | 116 | #### Esquema del objeto de configuración 117 | 118 | ```json 119 | { 120 | useNewUrlParser: true, 121 | useUnifiedTopology: true, 122 | dbName: 'blog' 123 | } 124 | ``` 125 | 126 | #### Método connect 127 | 128 | ```js 129 | const mongoose = require('mongoose'); 130 | 131 | const dbConnection = async () => { 132 | try { 133 | await mongoose.connect(process.env.MONGO_URL, { 134 | useNewUrlParser: true, 135 | useUnifiedTopology: true, 136 | dbName: 'blog' 137 | }); 138 | console.log('Connected to database'); 139 | } catch (error) { 140 | console.log(error); 141 | } 142 | }; 143 | ``` 144 | 145 | ### Model 146 | 147 | Un model o modelo, es una clase con la que podemos crear instancias de documentos. En mongoose, los modelos se crean a partir de un Schema, que es una clase que nos permite definir la estructura de los documentos. 148 | 149 | Para crear el Schema, usamos el método `model` de mongoose, que recibe dos parámetros: 150 | 151 | - Los datos que queremos almacenar en la colección. 152 | - El nombre de la colección. 153 | - La database a la que pertenece la colección. 154 | 155 | ```js 156 | const mongoose = require('mongoose'); 157 | 158 | const UserSchema = new mongoose.Schema({ 159 | name: String, 160 | email: String, 161 | password: String 162 | },{ 163 | collection: 'users', 164 | database: 'blog' 165 | }); 166 | ``` 167 | 168 | Una vez creado el Schema, podemos crear el modelo, para ello, usamos el método `model` de mongoose, que recibe dos parámetros: 169 | 170 | - El nombre del modelo. 171 | - El Schema. 172 | 173 | ```js 174 | const User = mongoose.model('User', UserSchema); 175 | ``` 176 | 177 | ### Añadir al documento 178 | 179 | Para añadir un documento a la colección, creamos una instancia del modelo, con los nuevos datos y añadimos a nuestra BBDD usando el método `save` que nos devuelve una promesa, por lo tanto, usaremos async/await para manejar la promesa. 180 | 181 | ```js 182 | 183 | const user = new User({ 184 | name: 'John Doe', 185 | email: 'example@example', 186 | password: '123456', 187 | }); 188 | 189 | try{ 190 | await user.save(); 191 | } catch(error){ 192 | console.log(error); 193 | } 194 | ``` 195 | 196 | ### Consultar entradas del documento 197 | 198 | Para consultar los datos de la entrada, usamos el método `find` del modelo, este método busca en la colección que llamamos, y recibe un objeto, en este caso, le pasamos un objeto vacío, para que nos devuelva todos las entradas del documento. 199 | 200 | ```js 201 | 202 | try{ 203 | const users = await User.find(); 204 | console.log(users); 205 | } catch(error){ 206 | console.log(error); 207 | } 208 | ``` 209 | 210 | Buscar un documento por un campo específico, tan solo tenemos que pasarle un objeto con el campo y el valor que queremos buscar, o podemos hacer uso del destructuring para pasarle el campo y el valor. 211 | 212 | ```js 213 | 214 | try{ 215 | const users = await User.find({name: 'John Doe'}); 216 | console.log(users); 217 | } catch(error){ 218 | console.log(error); 219 | } 220 | 221 | try{ 222 | const name = 'John Doe'; 223 | const users = await User.find({name}); 224 | console.log(users); 225 | } catch(error){ 226 | console.log(error); 227 | } 228 | 229 | try{ 230 | const usuario = 'John Doe'; 231 | const users = await User.find({user: usuario}); 232 | console.log(users); 233 | } catch(error){ 234 | console.log(error); 235 | } 236 | 237 | ``` 238 | 239 | ### Actualizar entrada de un documento 240 | 241 | Para actualizar un documento, usamos el método `findByIdAndUpdate` del modelo, que recibe dos parámetros: 242 | 243 | - El id del valor que queremos actualizar. 244 | - Un objeto con los nuevos valores. 245 | - un objeto con la configuración, en este caso, le pasamos el parámetro `new` con el valor `true`, para que nos devuelva el documento actualizado. 246 | 247 | ```js 248 | try{ 249 | const newUserData = { 250 | id: '123456789', 251 | name: 'Jonathan Doe' 252 | } 253 | 254 | const updateUser = await User.findByIdAndUpdate(newUserData.id, newUserData, {new: true}); 255 | 256 | console.log(updateUser); 257 | 258 | } catch(error){ 259 | console.log(error); 260 | } 261 | ``` 262 | 263 | ### Eliminar un documento 264 | 265 | Para eliminar una entrada del documento, usamos el método `findByIdAndDelete` del modelo, que recibe un parámetro: 266 | 267 | - El id del valor que queremos eliminar. 268 | 269 | ```js 270 | 271 | try{ 272 | const id = '123456789'; 273 | 274 | const deleteUser = await User.findByIdAndDelete(id); 275 | 276 | console.log(deleteUser); 277 | 278 | } catch(error){ 279 | console.log(error); 280 | } 281 | ``` 282 | --------------------------------------------------------------------------------