Todos alguna vez hemos tenido la necesidad de hacer un login y register con un lenguaje de programación en especial, aunque tengamos experiencia en programación nos toca que abrir la documentación del nuevo lenguaje de estudio y nos toca que googlear; sobre todo, cuando la documentación se nos es difícil entender.
Así que es seguro que has llegado a este post debido a eso; y te invito a que termines la lectura y cualquier duda, recomendación o crítica es bienvenida :)
Antes que antes que nada, te invito a que le eches un ojo a un post anterior donde explico de forma breve cómo funciona JWT
[Vuejs y JWT
Seguramente eres un usuario ya experimentado o simplemente un nuevo usuario que está aprendiendo Vuejs; este famoso y…
Antes de empezar, echemos un ojo a la estructura de carpetas de nuestro pequeño ejemplo de login y register con nodejs

Nodejs nos provee la facilidad de que agreguemos solo lo que necesitamos, con pocas lineas de código ya tenemos un servidor en nuestro pc.
- La carpeta public, es la carpeta que estará disponible para todo el mundo cuando entremos a localhost:3000 (local) en nuestro caso, un simple archivo html.
- Server es la carpeta que contiene todo el código que no está disponible para cualquiera, es decir, es el código que ejecutará nuestro servidor
Dentro de la carpeta server tenemos los modelos, que manejaremos con mongoose, la carpeta encargada de las rutas, y la carpeta config, que puede contener la configuración de nuestro proyecto, en este caso, las variables de entorno manejadas con process de nodejs; y por último nuestro archivo principal server.js que es el archivo que une toda nuestra app.
Antes que nada tenemos que tener presente que necesitamos de tres componentes básicos para el funcionamiento.
- Un modelo: que se encarga de la gestión de los usuarios en mongodb
- Rutas: que son las encargadas de recibir la información enviada desde nuestro frontend y de devolvernos la información que genera nuestro backend.
- Conexión a la db: en este caso, con mongodb, aunque fácilmente puede ser con otro tipo de db, incluso Redis, mySql, o la que te sea conveniente.
Con estas tres cosas presentes en nuestro proyecto podremos hacer un sencillo, funcional y seguro login y register.
Empecemos con el código
En nuestro archivo server/server.js consta de varias partes las cuales, las explico de forma breve
Exportamos nuestra dependencias al inicio del archivo. Estas dependencias serán las que nos ayuden al funcionamiento correcto del server.
require('./config/config');
const express = require('express')
const app = express()
const mongoose = require('mongoose');
const bodyParser = require('body-parser')
const path = require('path');
El siguiente código es básicamente un middleware que nos ayuda a “parsear” los datos que recibimos a través del protocolo http.
PD: para más información leer la descripción de npm
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))// parse application/json
app.use(bodyParser.json())
Como buenas prácticas se nos recomienda separar nuestro proyecto por partes, en este caso, las rutas, es decir; los controladores deben de tener su propio archivo y exportarlos como un “middleware” al archivo server.js
// Configuracion global de rutas
app.use(require('./routes/index'));
Las siguientes líneas nos indica una ruta raíz, es decir, al momento que vamos a localhost:3000 nodejs nos servirá un archivo html sencillo.
Para más información vista la documentación oficial
let renderHTML = path.resolve(__dirname, '../public/index.html');app.get('/', function (req, res) { res.sendFile(renderHTML);})
La conexión a nuestra db no debe de faltar
mongoose.connect(process.env.URLDB, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true}, (err) => { if (err) throw err;
console.log("Base de datos online");});
Process.env.URLDB y process.env.PORT son una variable de entorno que veremos más adelante.
Y por último, el código para lanzar nuestro servidor
app.listen(process.env.PORT, ()=> {
console.log("Escuchando en puerto 3000");
})
Nuestro archivo server.js nos quedaría de la siguiente manera.

Creando nuestro archivo de configuración
todo proyecto web, requiere de configuraciones básicas disponibles desde cualquier lado de nuestro proyecto, y la mayoría de entornos de desarrollo contienen estas variables de configuración, como en laravel, tenemos un archivo llamado .env, y en nodejs no es la excepción; a estas configuraciones se les conoce como variables de entorno, y es acá donde definimos por ejemplo, nuestra cadena de conexión, un seed de encriptación de contraseña; entre otros.
Dentro de la carpeta /config/config.js definimos:
- Puerto de acceso desde nuestro localhost
process.env.PORT = process.env.PORT || 3000;
- Entorno de desarrollo (dev, local, prod, test)
process.env.NODE_ENV = process.env.NODE_ENV || 'dev';
- Cadena de conexión a nuestra bd.
let urlDB = "";if (process.env.NODE_ENV === 'dev') {
urlDB = "mongodb://localhost:27017/mediumNodeLogin";
} else {
urlDB = "here write the mongo connection with mongo atlas and other type of connection mode"
};process.env.URLDB = urlDB;
- Caducidad del token
process.env.CADUCIDAD_TOKEN = '48h';
- Seeds de autenticación (si deseas saber más sobre este tema visita: click aqui)
process.env.SEED_AUTENTICACION = process.env.SEED_AUTENTICACION || 'este-es-el-seed-desarrollo';
Al final nos quedara un archivo de configuración así:

Creando modelo Usuario
Los modelos los podemos nombrar con cualquier nombre, en este caso, modelo Usuario; que facilmente podría llamarse modelo User, o como se les sea más conveniente.
Empezamos importando los archivos js que requerimos para hacer un modelo con mongoose
const mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
la importación de uniqueValidator lo que hace es facilitarnos de escribir por nosotros mismos la validación de que un campo sea único, en este caso, el campo email, email es de suma importancia que sea único en nuestro modelo Usuario.
Por si desean saber más sobre cómo funciona visitar la documentación de npm
Continuamos definiendo un Object que nos ayude a validar que tipo de rol será válido para usuario. Por ejemplo, queremos asegurarnos de que solo existan dos tipos de roles, ADMIN Y USER y que así, evitamos que nos envíen un role que no esta permitido en nuestra db, por ejemplo el role CONSERJE.
Además, definimos un Schema de mongoose.
let rolesValidos = { values: ["ADMIN", "USER"],
message: '{VALUE} no es un role válido'}
let Schema = mongoose.Schema;
Luego continuamos definiendo un nuevo Schema; recuerda que acá es donde definimos los campos de nuestro document de mongodb; es decir que si queremos que exista el campo por poner un ejemplo de estado de si está o no activo el usuario; debe de ir dentro del modelo Usuario
let usuarioSchema = new Schema({nombre: {
type: String,
required: [true, 'El nombre es necesario'],},email: {
type: String,
unique: true,
required: [true, "El correo es necesario"],
},password: {
type: String,
required: [true, "Le contraseña es obligatoria"],},role: {
type: String,
default: 'USER',
required: [true],
enum: rolesValidos,},});
Ahora, el modelo Usuarios también se encarga de traernos la data de cada usuario registrado al momento de hacer login; en este caso, nos devolvería toda la información del modelo, incluyendo la contraseña, aunque la contraseña estará encriptada con un hash jwt ¿queremos que cualquier usuario tenga acceso a la cadena encriptada de nuestros usuarios?
La respuesta es un rotundo no, por lo cual, al momento de hacer login, tenemos que asegurarnos que este campo no nos lo devuelva, motivo por el cual, tendremos que eliminarlo de la response de la petición http
// elimina la key password del objeto que retorna al momento de crear un usuariousuarioSchema.methods.toJSON = function() { let user = this;
let userObject = user.toObject();
delete userObject.password; return userObject;}
y por último, agregamos el plugin de validación única y exportamos el modelo recién creado
usuarioSchema.plugin(uniqueValidator, {
message: '{PATH} debe de ser único'
})
module.exports = mongoose.model('Usuario', usuarioSchema)
Al final nos quedaría un modelo como el siguiente

Rutas de nuestra app
Como se observó en la estructura de carpetas, dentro de la carpeta routes contamos con 3 archivos, el index, que es el archivo principal donde importamos el resto de rutas; seguido del archivo login.js y register.js que contiene el código pertinente para hacer login y register.
Nuestro archivo /routes/index.js contiene el siguiente código
const express = require('express')
const app = express()app.use(require('./login'));
app.use(require('./register'));module.exports = app;
Importamos express y app para hacer uso del middleware .use y al final, exportamos app con module.exports
Ruta de login
Empezamos importando a nuestro archivo /routes/login.js las dependencias que se requieren para el funcionamiento; en este caso ya requerimos del modelo recién creado; ya que es a través de este modelo que vamos a poder guardar, editar, buscar, y listas los usuarios registrados.
const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const Usuario = require('./../models/usuario');
const app = express();
Es de suma importancia que nuestros modelos sean asignados con la primera letra en mayúscula, esto para que podamos diferenciar el uso del modelo en el código.
Las rutas se crean con la siguiente sintaxis:
app.post('/login', function (req, res) { // here write code
})
donde app es la referencia a express y .post es el verbo http que requerimos para está ruta, es decir, que si deseamos un get, un update o delete, .post debe de ser reemplazado por cualquier otro verbo requerido.
dentro recibe dos parámetros (en este caso) el primero ‘/login’ que es el nombre que le asignamos a la ruta, y como segundo parámetro, recibe una función donde se recibe como parametro req y res, req es la encargada de recibir la data proveniente del frontend, y res es quien se encarga de devolvernos un status code y/o header que establezcamos.
Dentro de la ruta, debemos de obtener la información proveniente del frontend.
let body = req.body;
y guardamos la información que recibimos.
Usuario.findOne({ email: body.email }, (erro, usuarioDB)=>{ if (erro) {
return res.status(500).json({
ok: false,
err: erro
})
}// Verifica que exista un usuario con el mail escrita por el usuario. if (!usuarioDB) { return res.status(400).json({
ok: false,
err: {
message: "Usuario o contraseña incorrectos"
}
})
}// Valida que la contraseña escrita por el usuario, sea la almacenada en la db if (! bcrypt.compareSync(body.password, usuarioDB.password)){ return res.status(400).json({
ok: false,
err: {
message: "Usuario o contraseña incorrectos"
} }); }// Genera el token de autenticación let token = jwt.sign({
usuario: usuarioDB,
}, process.env.SEED_AUTENTICACION, {
expiresIn: process.env.CADUCIDAD_TOKEN
}) res.json({
ok: true,
usuario: usuarioDB,
token,
})
})
```js
y al finalizar, exportamos nuevamente app
```js
module.exports = app;
Al finalizar, nos quedará una ruta como esta:

Ruta register
La ruta register es similar a la ruta login, a diferencia que acá es donde se tiene que encriptar la contraseña, y es donde se hace uso de bcrypt
const express = require('express');
const bcrypt = require('bcrypt');
const Usuario = require('./../models/usuario');
const app = express();app.post('/register', function (req, res) { let body = req.body;
let { nombre, email, password, role } = body; let usuario = new Usuario({
nombre,
email,
password: bcrypt.hashSync(password, 10),
role
});usuario.save((err, usuarioDB) => { if (err) {
return res.status(400).json({
ok: false,
err,
});
} res.json({
ok: true,
usuario: usuarioDB
});
})
});module.exports = app;
Al final, nos quedará algo como esto:

Si probamos nuestro login con postman nos debe de devolver algo similar a:
Login

Register

Error register

Y ya tenemos listo un login y register usando nodejs, express, jwt y Mongodb :)