La creación de APIs RESTful es una habilidad fundamental para cualquier desarrollador web moderno. En este contenido, exploraremos cómo implementar el método POST para crear nuevos usuarios en nuestra API, un paso esencial para construir aplicaciones que permitan el registro de usuarios. Aprenderás a recibir datos, validarlos y almacenarlos correctamente, sentando las bases para operaciones CRUD completas.
¿Cómo implementar el método POST para crear usuarios en una API?
Cuando desarrollamos una API, necesitamos permitir que los clientes puedan crear nuevos recursos. En este caso, implementaremos un endpoint que utiliza el método HTTP POST para crear nuevos usuarios en nuestra aplicación.
Para comenzar, necesitamos crear un nuevo punto de entrada en nuestra aplicación. Aunque ya teníamos un endpoint para obtener usuarios con GET, ahora crearemos uno que utilice POST para la misma ruta:
app.post('/users',(req, res)=>{// Código para crear un nuevo usuario});
Este es un ejemplo claro de cómo una misma ruta puede tener diferentes comportamientos dependiendo del método HTTP que se utilice, uno de los principios fundamentales de las APIs RESTful.
¿Cómo obtener y procesar los datos enviados por el cliente?
Cuando un cliente envía una solicitud POST, generalmente incluye datos en el cuerpo de la solicitud. Gracias a la integración de BodyParser en nuestra aplicación, podemos acceder fácilmente a estos datos:
const newUser = req.body;
Esta simple línea nos permite capturar toda la información que el cliente está enviando. La estructura esperada para un usuario podría ser algo como:
Antes de guardar un nuevo usuario, es crucial validar que los datos recibidos cumplan con nuestros requisitos. Algunas validaciones comunes incluyen:
Verificar que el nombre tenga al menos 3 caracteres
Comprobar que el email tenga un formato válido
Asegurarse de que el ID sea único
Para implementar estas validaciones, podríamos usar código como el siguiente:
// Validación del nombreif(!newUser.name|| newUser.name.length<3){return res.status(400).json({error:"El nombre debe tener al menos 3 caracteres"});}// Validación del email con expresión regularconst emailRegex =/^[^\s@]+@[^\s@]+\.[^\s@]+$/;if(!newUser.email||!emailRegex.test(newUser.email)){return res.status(400).json({error:"El email no es válido"});}
¿Cómo leer y actualizar los datos existentes?
Para guardar un nuevo usuario, primero necesitamos leer los usuarios existentes. Utilizaremos el módulo fs (File System) de Node.js para esto:
const fs =require('fs');fs.readFile(userFilePath,'utf-8',(error, data)=>{if(error){return res.status(500).json({error:"Error con conexión de datos"});}const users =JSON.parse(data);// Aquí continuamos con el proceso});
Una vez que tenemos los usuarios existentes, podemos agregar el nuevo usuario y guardar la información actualizada:
// Agregar el nuevo usuariousers.push(newUser);// Guardar la información actualizadafs.writeFile(userFilePath,JSON.stringify(users,null,2),(error)=>{if(error){return res.status(500).json({error:"Error al guardar el usuario"});}// Responder con el usuario creado res.status(201).json(newUser);});
Utilizamos el código de estado HTTP 201 (Created) para indicar que el recurso se ha creado correctamente, siguiendo las mejores prácticas de diseño de APIs RESTful.
¿Cómo probar nuestro endpoint POST?
A diferencia de los endpoints GET, no podemos probar un endpoint POST simplemente visitando una URL en el navegador. Necesitamos una herramienta como Postman:
Crear una nueva solicitud en Postman
Seleccionar el método POST
Ingresar la URL de nuestro endpoint (por ejemplo, http://localhost:3000/users)
En la pestaña "Body", seleccionar "raw" y "JSON"
Ingresar los datos del nuevo usuario en formato JSON
Enviar la solicitud
Si todo funciona correctamente, recibiremos una respuesta con código 201 y los datos del usuario creado. Podemos verificar que el usuario se haya guardado correctamente haciendo una solicitud GET a /users.
¿Qué mejoras podríamos implementar?
Aunque nuestro endpoint funciona, hay varias mejoras que podríamos implementar:
Validación de ID único: Actualmente, no estamos verificando si el ID ya existe, lo que podría causar problemas.
Generación automática de ID: En lugar de requerir que el cliente proporcione un ID, podríamos generarlo automáticamente.
Manejo de errores más robusto: Podríamos implementar un sistema más completo para manejar diferentes tipos de errores.
Encapsulación de código repetido: Notamos que hay código que se repite (como la lectura del archivo) que podría encapsularse en funciones reutilizables.
Estas mejoras nos ayudarían a crear una API más robusta y mantenible, siguiendo los principios de diseño de software como DRY (Don't Repeat Yourself) y la separación de responsabilidades.
La implementación del método POST es solo el comienzo. En próximas lecciones, aprenderemos a utilizar el método PUT para actualizar información existente, completando así más operaciones CRUD en nuestra API.
¿Has implementado validaciones en tus APIs? Comparte en los comentarios cómo manejas la validación de datos en tus proyectos y qué estrategias utilizas para garantizar la integridad de la información.
Por acá mi aporte para la validación del campo name y emailapp.post('/users', (req, res) => { const newUser = req.body; const name = newUser.name; const email = newUser.email; const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if(!newUser || Object.keys(newUser).length === 0){ return res.status(400).json({error: 'No se recibieron datos'}); }
if(name.length < 3){ return res.status(400).json({ error: 'El campo \'name\' debe ser igual o mayor a 3 caracteres'}) }
if(!regex.test(email)){ return res.status(400).json({ error: 'El campo \'email\' no cumple con la estructura esperada de un correo'}) }});
app.post('/users',(req, res)=>{const newUser = req.body;const name = newUser.name;const email = newUser.email;const regex =/^[^\s@]+@[^\s@]+\.[^\s@]+$/;if(!newUser ||Object.keys(newUser).length===0){return res.status(400).json({error:'No se recibieron datos'});}if(name.length<3){return res.status(400).json({error:'El campo \'name\' debe ser igual o mayor a 3 caracteres'})}if(!regex.test(email)){return res.status(400).json({error:'El campo \'email\' no cumple con la estructura esperada de un correo'})}});
const emailRegex =/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;functionvalidateEmail(email){return emailRegex.test(email);}app.post('/users',(req, res)=>{const{ name, email }= req.body;const errors =[];if(!name ||!email){return res.status(400).json({error:'Los campos name y email son obligatorios'});}if(name && name.length<10){ errors.push('El campo Nombre debe tener al menos 10 carácteres');}if(email &&!validateEmail(email)){ errors.push('El campo Email ingresado no es válido');}if(errors.length>0){return res.status(400).json({ errors });} fs.readFile(usersFilePath,'utf-8',(error, data)=>{if(error){return res.status(500).json({error:'Error con conexión de datos.'});}const users =JSON.parse(data);const newId = users.length>0? users[users.length-1].id+1:0const newUser ={id: newId,...req.body}; users.push(newUser); fs.writeFile(usersFilePath,JSON.stringify(users,null,2),(error)=>{if(error){return res.status(500).json({error:'Hubo un error la guardar al usuario.'});} res.status(201).json(newUser)});});```app.post('/users',(req, res)=>{const{ name, email }= req.body;const errors = \[];if(!name ||!email){return res.status(400).json({error:'Los campos name y email son obligatorios'});}if(name && name.length<10){ errors.push('El campo Nombre debe tener al menos 10 carácteres');}if(email &&!validateEmail(email)){ errors.push('El campo Email ingresado no es válido');}if(errors.length>0){return res.status(400).json({ errors });} fs.readFile(usersFilePath,'utf-8',(error, data)=>{if(error){return res.status(500).json({error:'Error con conexión de datos.'});}const users =JSON.parse(data);const newId = users.length>0? users\[users.length-1].id+1:0const newUser ={id: newId,...req.body}; users.push(newUser); fs.writeFile(usersFilePath,JSON.stringify(users,null,2),(error)=>{if(error){return res.status(500).json({error:'Hubo un error la guardar al usuario.'});} res.status(201).json(newUser)});});
Hola, podrian explicarme como funciona esa linea de caracteres especiales que llama regex, nunca habia visto eso. Gracias
Para resolver esto cree un archivo llamado utils.js donde puse mis funciones para validae email y nombre y las exporte para usarlas en el archivo app.js
Y luego asi quedo mi metodo de post en el archivo app.js
app.post('/users',(req,res)=>{const newUser = req.body;if(!checkEmail(req.body.email)){return res.status(500).json({error:'El email no tiene el formato valido'});}if(!checkName(req.body.name)){return res.status(500).json({error:'El nombre no tiene la longitud valida'});} fs.readFile(usersFilePath,'utf-8',(err,data)=>{if(err){return res.status(500).json({error:'Error con conexion de datos'});}const users =JSON.parse(data); users.push(newUser); fs.writeFile(usersFilePath,JSON.stringify(users,null,2),err=>{if(err){return res.status(500).json({error:'Error al guardar el usuario'});} res.status(201).json({newUser});});});});
functionvalidarEmail(email){const regex =/^[^\s@]+@[^\s@]+\.[^\s@]+$/;return regex.test(email);}functionvalidarNombre(nombre){const regex =/^[a-zA-Z]+\s[a-zA-Z]+$/;return regex.test(nombre);}//GET con base en un archivoapp.get('/users',(req, res)=>{ fs.readFile(usersFilePath,'utf-8',(err, data)=>{if(err){return res.status(500).json({error:'Error en la conexion de datos'});}const users =JSON.parse(data); res.json(users);});});//POST con base en un archivoapp.post('/users',(req, res)=>{//se leen los usuarios existentes fs.readFile(usersFilePath,'utf-8',(err, data)=>{if(err){return res.status(500).json({error:'Error en la conexion de datos'});}const users =JSON.parse(data);//se valida que la informacion del requerimiento sea validaconst newUser = req.body;if(!newUser ||Object.keys(newUser).length===0){return res.status(400).json({error:'No se recibieron datos'});}//comprueba no repeticion del indice const indice = users.findIndex(u=> u.id=== newUser.id)if(indice !==-1){return res.status(500).json({error:'El indice ya existe'});}//valida si el nombre tiene al menos dos partesif(!validarNombre(newUser.name)){return res.status(500).json({error:'Nombre invalido'});}//valida que la sintaxis del email este bienif(!validarEmail(newUser.email)){return res.status(500).json({error:'email invalido'});}//se agrega el nuevo usuario al arreglo de usuarios. Y se guarda en el archivo users.push(newUser); fs.writeFile(usersFilePath,JSON.stringify(users,null,4),err=>{if(err){return res.status(500).json({error:'Error al guardar el usuario'});}});//se envia el resultado que el usuario ha sido creado. res.status(201).json(newUser);});});
functionvalidarEmail(email){ const regex =/^\[^\s@]+@\[^\s@]+\\.\[^\s@]+$/; return regex.test(email);}functionvalidarNombre(nombre){ const regex =/^\[a-zA-Z]+\s\[a-zA-Z]+$/; return regex.test(nombre);}*//GET con base en un archivo*app.get('/users',(req, res)=>{  fs.readFile(usersFilePath,'utf-8',(err, data)=>{ if(err){ return res.status(500).json({error:'Error en la conexion de datos'}); } const users =JSON.parse(data);  res.json(users); });});*//POST con base en un archivo*app.post('/users',(req, res)=>{ *//se leen los usuarios existentes*  fs.readFile(usersFilePath,'utf-8',(err, data)=>{ if(err){ return res.status(500).json({error:'Error en la conexion de datos'}); } const users =JSON.parse(data); *//se valida que la informacion del requerimiento sea valida* const newUser = req.body;   if(!newUser ||Object.keys(newUser).length===0){ return res.status(400).json({error:'No se recibieron datos'}); } *//comprueba no repeticion del indice*   const indice = users.findIndex(u=> u.id=== newUser.id) if(indice !==-1){ return res.status(500).json({error:'El indice ya existe'}); } *//valida si el nombre tiene al menos dos partes* if(!validarNombre(newUser.name)){ return res.status(500).json({error:'Nombre invalido'}); } *//valida que la sintaxis del email este bien* if(!validarEmail(newUser.email)){ return res.status(500).json({error:'email invalido'}); }   *//se agrega el nuevo usuario al arreglo de usuarios. Y se guarda en el archivo*  users.push(newUser);  fs.writeFile(usersFilePath,JSON.stringify(users,null,4),err=>{ if(err){ return res.status(500).json({error:'Error al guardar el usuario'}); } }); *//se envia el resultado que el usuario ha sido creado.*  res.status(201).json(newUser); });});  ``` 
Validacion:
app.post("/users",(req, res)=>{const{ name, email }= req.body;const regex =/^[^\s@]+@[^\s@]+\.[^\s@]+$/;if(!newUser ||Object.keys(newUser).length===0)return res.status(400).json({message:"No me haz enviado nada"});if(name.length<=3)return res.status(400).json({message:"El nombre es muy corto"});if(!regex.test(email))return res.status(400).json({message:"El Email no es valido"});return res.json(newUser);});
Les comparto mis validaciones:
VALIDACIÓN DE NOMBRE:
VALIDACIÓN DE EMAIL CON EXPRESIONES REGULARES (REGEX)
FuenteS:
Una forma concisa y entendible de hacer la validación es con Regex y la forma de implementarlo aún más:
app.post('/users', (req, res) => { const newUser = req.body; if (!newUser || !newUser.name || !newUser.email) { return res.status(400).json({ error: 'Name and email are required' }); } if (newUser.name.length < 3) { return res .status(400) .json({ error: 'Name must be at least 3 characters long' }); } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(newUser.email)) { return res.status(400).json({ error: 'Invalid email format' }); } fs.readFile(usersFilePath, 'utf-8', (err, data) => { if (err) { return res.status(500).json({ error: 'Error reading users file' }); } try { const users = JSON.parse(data); users.push(newUser); fs.writeFile( usersFilePath, JSON.stringify(users, null, 2), (writeErr) => { if (writeErr) { return res .status(500) .json({ error: 'Error writing to users file' }); } res .status(201) .json({ message: 'User added successfully', user: newUser }); } ); } catch (parseError) { res.status(500).json({ error: 'Error parsing users data' }); } });});
Aquí mi aporte con validaciones de formato y de propiedades requeridas:
app.post('/users',(req, res)=>{const newUser = req.body;const{name, email}= newUser;const regex_email =/^[^\s@]+@[^\s@]+\.[^\s@]+$/;const regex_name =/^[a-zA-Z\s]{3,}$/;// Check if all required fields are presentif(!name){return res.status(400).json({error:'Missing required field: name'});}elseif(!email){return res.status(400).json({error:'Missing required field: email'});};//Data validationif(!newUser ||Object.keys(newUser).length===0){return res.status(400).json({error:'No user data provided'});}if(!name ||!regex_name.test(name)){return res.status(400).json({error:'User name must be at least 3 characters long and contain only letters'});}if(!email ||!regex_email.test(email)){return res.status(400).json({error:'Invalid email format'});} fs.readFile(usersFilePath,'utf-8',(err, data)=>{if(err){return res.status(500).json({error:'Error with data connection'});}const users =JSON.parse(data); newUser.id= users.length+1;// Simple ID assignment users.push(newUser); fs.writeFile(usersFilePath,JSON.stringify(users,null,2),(err)=>{if(err){return res.status(500).json({error:'Error saving user'});} res.status(201).json(newUser);});});});
app.post("/users",async(req, res)=>{try{const{ name, email }= req.body;if(!name ||!email){return res
.status(400).json({error:"Faltan campos obligatorios: name y email"});}const data =await fsPromises.readFile(usersFilePath,"utf-8");const users =JSON.parse(data);const lastId = users.length>0? users[users.length-1].id:0;const newUser ={id: lastId +1, name, email,}; users.push(newUser);await fsPromises.writeFile(usersFilePath,JSON.stringify(users,null,2)); res.json(users);}catch(err){console.error("Error al leer users.json:", err); res
.status(500).json({error:"Error al leer el archivo o archivo corrupto"});}});```con este codigo solo deben enviar el name y el email el id nuevo se calcula obteniendo el ultimo y sumando 1, tambien lo podemos hacer con un uuid pero esto es meramente educativo para seguir el video