Stack de tecnologías
- Node.js
- Express
- MongoDB
Configuración inicial
1
2
npm init -y
- Este comando genera el archivo
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "backend-todo-univo",
"version": "1.0.0",
"description": "Proyecto BackEnd TODO UNIVO",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["Rest", "UNIVO"],
"author": "Jaime Guevara",
"license": "ISC"
}
- Instalación de dependencias
1
2
npm i bcryptjs cookie-parser cors dotenv express express-validator jsonwebtoken mongoose
npm i -D nodemon
- Modificar archivo
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"name": "apirest-univo",
"version": "1.0.0",
"type": "module",
"description": "Proyecto API TODO UNIVO",
"main": "index.js",
"scripts": {
"dev": "nodemon index.js",
"start": "node index.js"
},
"keywords": [
"Rest",
"UNIVO"
],
"author": "Jaime Guevara",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"express-validator": "^6.14.2",
"jsonwebtoken": "^8.5.1",
"mongoose": "^6.5.2"
},
"devDependencies": {
"nodemon": "^2.0.19"
}
}
- Crear archivo
.gitignore
1
2
3
node_modules
.env
- Archivo
index.js
1
2
3
4
5
6
import express from "express"
const app = express()
app.listen(3000, () => console.log("🔥🔥🔥 servidor disponible en: http://localhost:3000"))
- Ejecutar servidor con:
npm run dev
- Crear/verificar la siguiente estructura de archivos y directorios
Base de datos MongoDB
- Crear base de datos en mongo.db creando cuenta en https://www.mongodb.com/ donde se crea un usuario y se conecta a la base de datos con la opción Connect your application
- Copiar la URI, después se modificara con el usuario y password generado
Variables de entorno y Moongoose
- Modificar archivo
.env
sustituyendo sus credenciales
1
2
DB_URI=mongodb+srv://<usuario>:<password>@cluster0.3kuar1l.mongodb.net/todo_v1
- Crear archivo
database/connect.js
1
2
3
4
5
6
7
8
9
import mongoose from "mongoose"
try {
await mongoose.connect(process.env.DB_URI)
console.log("Conectado a la base de datos 😎")
} catch (error) {
console.log("Error de conexion a mongoDB" + error)
}
- Modificar
index.js
1
2
3
4
5
6
7
8
9
import 'dotenv/config'
import './database/connect.js'
import express from "express"
const app = express()
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("🔥🔥🔥 servidor disponible en: http://localhost:" + PORT))
Schema & Models
- Schema: Con Mongoose, todo se deriva de un esquema.
- Cada esquema se asigna a una colección MongoDB y define la forma de los documentos dentro de esa colección.
- Para usar nuestra definición de esquema, necesitamos convertirla a un modelo con el que podamos trabajar
- Crear archivo
models/user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import mongoose from "mongoose"
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
trim: true,
unique: true,
lowercase: true,
index: { unique: true },
},
password: {
type: String,
required: true,
},
});
export const User = mongoose.model("User", userSchema);
Routes
- Crear archivo
routes/auth_route.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import express from 'express'
const router = express.Router()
router.get("/login", (req, res) => {
res.json({ok:"login"})
})
router.post("/register", (req, res) => {
res.json({ok:true})
})
export default router
- Modificar
index.js
1
2
3
4
5
6
7
8
9
10
11
12
import 'dotenv/config'
import './database/connect.js'
import express from "express"
import authRoutes from "./routes/auth_route.js"
const app = express()
app.use('/', authRoutes)
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("🔥🔥🔥 servidor disponible en: http://localhost:" + PORT))
- Los navegadores solo permiten realizar peticiones GET, para realizar peticiones POST es necesario un programa como POSTMAN o INSOMNIA
- Agregamos version a nuestra api modificando archivo
index.js
1
2
3
4
5
6
7
8
9
10
11
12
import 'dotenv/config'
import './database/connect.js'
import express from "express"
import authRoutes from "./routes/auth_route.js"
const app = express()
app.use('/api/v1/auth', authRoutes)
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("🔥🔥🔥 servidor disponible en: http://localhost:" + PORT))
Controllers
- Utilizamos controladores para manejar la lógica de respuesta de las rutas, crear archivo
controllers/auth_controller.js
1
2
3
4
5
6
7
8
export const register = async (req, res) => {
res.json({ok:true})
};
export const login = async (req, res) => {
res.json({ok:true})
};
- Modificar archivo
routes/auth_route.js
1
2
3
4
5
6
7
8
9
10
11
import express from 'express'
import { login, register } from '../controllers/auth_controller.js'
const router = express.Router()
router.get("/login", login)
router.post("/register", register)
export default router
Verificar envió de datos JSON
- Modificar
controllers/auth_controller.js
1
2
3
4
5
6
7
8
export const register = async (req, res) => {
console.log(req.body)
res.json({ok:true})
};
export const login = async (req, res) => {
res.json({ok:true})
};
- Probar envió de datos formato JSON con POSTMAN
- En la consola aparecerá undefined, eso es debido a que express no esta configurado para recibir peticiones en JSON, para habilitar esta lectura es necesario agregar un middleware al archivo
index.js
1
2
3
4
5
6
7
8
9
10
11
12
import 'dotenv/config'
import './database/connect.js'
import express from "express"
import authRoutes from "./routes/auth_route.js"
const app = express()
app.use(express.json())
app.use('/api/v1', authRoutes)
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("🔥🔥🔥 servidor disponible en: http://localhost:" + PORT))
- Vista en consola
1
2
3
4
5
6
[nodemon] restarting due to changes...
[nodemon] starting `node index.js`
Conectado a la base de datos 😎
🔥🔥🔥 servidor disponible en: http://localhost:3000
{ email: 'jr.correo@correo.com', password: '123456' }
Validaciones
- Agregamos validaciones con ExpressValidator, modificamos
routes/auth_route.js
ya que hay que interceptar la respuesta antes de que llegue al controlador.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import express from 'express'
import { login, register } from '../controllers/auth_controller.js'
import { validatorExpress } from "../middlewares/validatorExpress.js";
import { body } from "express-validator";
const router = express.Router()
router.post("/register",
[
body("email", "Ingrese un email válido")
.trim()
.isEmail()
.normalizeEmail(),
body("password", "Contraseña mínimo 6 carácteres")
.trim()
.isLength({ min: 6 })
.custom((value, { req }) => {
if (value !== req.body.reEnterPassword) {
throw new Error("No coinciden las contraseñas")
}
return value;
}),
],
validatorExpress,
register
)
router.post(
"/login",
[
body("email", "Ingrese un email válido")
.trim()
.isEmail()
.normalizeEmail(),
body("password", "Contraseña mínimo 6 carácteres")
.trim()
.isLength({ min: 6 }),
],
validatorExpress,
login
)
export default router
- Crear archivo
middlewares/validatorExpress.js
1
2
3
4
5
6
7
8
9
import { validationResult } from "express-validator"
export const validatorExpress = (req, res, next) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ error: errors.array() })
}
next()
}
- Probar validaciones con Postman
Encriptación al password y comparación
- Modificar archivo
models/User.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import mongoose from "mongoose"
import bcrypt from "bcryptjs"
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
trim: true,
unique: true,
lowercase: true,
index: { unique: true },
},
password: {
type: String,
required: true,
},
});
userSchema.pre("save", async function (next) {
const user = this
if (!user.isModified("password")) return next()
try {
const salt = await bcrypt.genSalt(10)
user.password = await bcrypt.hash(user.password, salt)
next()
} catch (error) {
console.log(error)
throw new Error("Error al codificar la contraseña")
}
});
userSchema.methods.comparePassword = async function (candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password)
}
export const User = mongoose.model("User", userSchema)
Register y Login
- Modificar
controllers/auth_controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { User } from "../models/User.js"
export const register = async (req, res) => {
const { email, password } = req.body
try {
let usuario = new User({ email, password })
await usuario.save()
return res.status(201).json({ ok: true })
} catch (error) {
console.log(error)
return res.status(400).json({ error: error.message })
}
}
export const login = async (req, res) => {
try {
const { email, password } = req.body
let usuario = await User.findOne({ email });
if (!usuario)
return res.status(403).json({ error: "No existe este usuario" });
const respuestaPassword = await usuario.comparePassword(password);
if (!respuestaPassword)
return res.status(403).json({ error: "Contraseña incorrecta" });
return res.status(201).json({ ok: true })
} catch (error) {
console.log(error)
return res.status(403).json({ error: error.message })
}
}
JWT
Generar JWT para el login y register, modificar archivo
.env
1
2
3
DB_URI=mongodb+srv://<usuario>:<password>@cluster0.3kuar1l.mongodb.net/todo_v1
JWT_SECRET=SflKkkRJSMeKKF2QT4fwpMeJf36POk6yJV_a3dAA5c
- Modificar
controllers/auth_controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { User } from "../models/User.js"
import jwt from "jsonwebtoken"
export const register = async (req, res) => {
const { email, password } = req.body
try {
let usuario = new User({ email, password })
await usuario.save()
return res.status(201).json({ ok: true })
} catch (error) {
console.log(error)
return res.status(400).json({ error: error.message })
}
}
export const login = async (req, res) => {
try {
const { email, password } = req.body
let user = await User.findOne({ email })
if (!user)
return res.status(403).json({ error: "No existe este usuario" })
const respuestaPassword = await user.comparePassword(password)
if (!respuestaPassword)
return res.status(403).json({ error: "Contraseña incorrecta" })
// JWT
const token = jwt.sign({uid: user._id}, process.env.JWT_SECRET)
return res.json({ token })
return res.status(201).json({ ok: true })
} catch (error) {
console.log(error)
return res.status(403).json({ error: error.message })
}
}