Crea una cuenta o inicia sesión

¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera

Rutas para usuarios

5/29
Recursos

¿Cómo estructurar eficientemente una API en Node.js?

Cuando desarrollamos una API en Node.js, la organización y la separación de las responsabilidades son clave para su mantenimiento y escalabilidad. Al separar la lógica de datos de la lógica de negocio, conseguimos que las modificaciones en una parte no afecten directamente a la otra. Esto es esencial para mantener un código limpio y eficiente, especialmente en proyectos a largo plazo.

¿Cómo asegurar la independencia de la base de datos?

Asegurar que nuestra API no dependa de una base de datos específica es un reto común. La solución radica en crear controladores que no estén atados directamente a los datos. Al inyectar la dependencia de la base de datos, podemos cambiarla según sea necesario sin afectar al resto de la aplicación. Esto es útil, por ejemplo, en situaciones de testing donde no queremos utilizar la base de datos de producción.

¿Cómo transformar un controlador en una función configurable?

Para crear un controlador que acepte inyecciones de dependencia, lo convertimos en una función. Aquí te muestro cómo se logra con un ejemplo en JavaScript:

// Ejemplo de un controlador convertido en función
module.exports = (injectorStore) => {
  // Lógica del controlador
  const list = () => {
    // Usar el injector en lugar de una base de datos fija
    const store = injectorStore || defaultStore;
    return store.list();
  };

  return {
    list,
  };
};

De esta forma, el control de la base de datos pasa a ser flexible, permitiendo que el controlador pueda utilizar diferentes stores.

¿Cómo se implementan rutas asíncronas con manejo de errores?

Implementar rutas asíncronas que manejen errores de manera eficiente es fundamental para construir API robustas. Utilizando promesas y funciones asíncronas, podemos gestionar errores de manera clara y organizada.

Ejemplo de promesas en una ruta

El uso de promesas para manejar operaciones asíncronas permite un manejo de errores más claro. Con el siguiente código podemos ver cómo se realiza en JavaScript:

// Ejemplo de implementación asíncrona con manejo de errores
app.get('/users', async (req, res) => {
  try {
    const users = await store.list();
    res.status(200).json(users);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

Aquí, con async y await, controlamos el flujo asíncrono de la función y con try-catch, manejamos los posibles errores.

¿Cuál es el beneficio de las funciones asincrónicas en Node.js?

Las funciones asincrónicas permiten que el servidor maneje múltiples solicitudes al mismo tiempo sin bloquear el hilo principal. Esto incrementa la eficiencia y la capacidad de respuesta de una aplicación Node.js, esencial para servicios que esperan manejar un gran volumen de tráfico.

¿Qué pasos seguir para crear rutas adicionales?

El desarrollo de nuevas rutas para manejar diferentes operaciones CRUD (Create, Read, Update, Delete) es un paso lógico y necesario. Una buena práctica es definir cada operación en un controlador separado y asegurarse de manejar adecuadamente las promesas y errores.

Desafío para el desarrollador

Se te anima a crear las siguientes rutas:

  1. Añadir un nuevo usuario: Utiliza el método add en el controlador correspondiente.
  2. Eliminar usuarios: Implementa la ruta y lógica necesaria para manejar la eliminación segura de usuarios.

Con esa base, podrás expandir la API y mejorar su funcionalidad, acercándote cada vez más al desarrollo de una API compleja y robusta. ¡Sigue aprendiendo y aplicando este conocimiento para crear aplicaciones de calidad!

Aportes 47

Preguntas 12

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Podemos usar async/await si no queremos encadenar then y catch, wrappeando todo en un try catch

router.get("/", async (req, res) => {
    try {
        const list = await controller.list()
        response.success(req, res, list, 200)    
    } catch (error) {
        response.error(req, res, error.message, 500)
    }
});```


es muy facil perderte en esta clase!!

Les recomiendo que instalen JSON Viewer como extencion de Chrome para que analicen las respuestas de la API mas cómodamente

Una peticion GET en el navegador se vera algo asi;

Podemos simplificar el codigo del controller preguntando por injected store y retornar las funciones directamente como arrows

module.exports = (injectedStore) => {
  if(!injectedStore) injectedStore = require("../../../store/dummy");

  return {
    list: () => injectedStore.list(TABLE),
    get: id => injectedStore.get(TABLE, id)
  };
};

Buenos dias,

Yo lo hice de la siguiente forma, por que? usando express, esta la posibilidad de capturar los parametros enviados por el body con la linea 7. con prueba y error hasta que al final pude capturar el body.

router.use(express.json());

Code:
components/user/networks.js

Resultado:

Le solucione el reto asi:

Hola profesor, si nuestra función get() del archivo dummy.js se utiliza el async y await, entonces como nuestra función del controlador que espera esos datos tambien debería ser async y await ? porque solo veo que se usa al async y await en la función del archivo dummy y network y no en el controllador que tambien se debe esperar los datos del dummy, me puede explicar por favor.

Hola a todos, alguien tiene documentación o algo que me recomienden ya que la parte de inyeccion se me hizo un poco compleja.

Yo solucione la ruta del remove así;

async function remove(tabla, id){
  
   let table = await list(tabla);
   let index = table.indexOf(id);
   let remove = table.splice(index + 1, 1 );

   return {
       table,
       remove
   }

}

y en el navegador sale así;

// 20200814022329
// http://localhost:3000/api/user/remove/1

{
“table”: [
{
“id”: 2,
“name”: “miss”
}
],
“remove”: [
{
“id”: 1,
“name”: “miran”
}
]
}

Anadi una validacion para asegurarse de que se recibe un numero

async function get(tabla, id) {
	let col = await list(tabla)
	let numId = parseInt(id)

	if (isNaN(numId)) {
		throw new Error('Parameter is not a number')
	}

	return col.filter(item =>  item.id === numId)[0] || null
}

Mi solución

// network.js

const express = require("express")

const response = require("../../../network/response");
const Controller = require("./index")

const router = express.Router();

router.get("/", (req, res) => {
    Controller.list()
        .then((list) => {
            response.success(req, res, list, 200)
        }).catch(err => {
            response.error(req, res, err.message, 500)
        })
})

router.get("/:id", (req, res) => {
    Controller.get(req.params.id)
        .then((user) => {
            response.success(req, res, user, 200)
        }).catch(err => {
            response.error(req, res, err.message, 500)
        })
})

router.get("/upsert/:id/:name", (req, res) => {
    Controller.upsert(req.params.id, req.params.name)
        .then((user) => {
            response.success(req, res, user, 200)
        }).catch(err => {
            response.error(req, res, err.message, 500)
        })
})

router.get("/delete/:id", (req, res) => {
    Controller.remove(req.params.id)
        .then((user) => {
            response.success(req, res, user, 200)
        }).catch(err => {
            response.error(req, res, err.message, 500)
        })
})

module.exports = router
// controller.js

const TABLE = "user"

module.exports = (injectedStore) => {
    let store = injectedStore
    if (!store) {
        store = require("../../../store/dummy")
    }

    return {
        list: () => store.list(TABLE),
        get: (id) => store.get(TABLE, id),
        upsert: (id, name) => store.upsert(TABLE, id, name),
        remove: (id) => store.remove(TABLE, id)
    }
}
// dummy.js

const db = {
    "user": [
        {
            id: "1",
            name: "Enrique"
        }
    ]
}

const list = async (table) => {
    return db[table];
}

const get = async (table, id) => {
    let collection = await list(table);
    return collection.find(item => item.id === id) || null
}

const upsert = async (table, id, name) => {
    let newUser = { id: id, name: name }
    db[table].push(newUser)
}

const remove = async (table, id) => {
    db[table].splice(Number(id) - 1, 1)
    return true
}


module.exports = {
    list,
    get,
    upsert,
    remove
}

Yo lo solucione asi:

dummy.js

const db = {
    'user': [
        { id: '1', name: 'Carlos'},
        { id: '2', name: 'Fernando'},
    ],
};

async function list(tabla) {
    return db[tabla];
};
async function get(tabla, id) {
    let col = await list(tabla);
    return col.filter( item => item.id === id)[0] || null;
};

async function upsert(tabla, data) {

    console.log(data)
    db[tabla].push(data);
    return data
}

async function remove(id) {

    for(let i = 0; i < db.user.length; i++){
        if(db.user[i].id == '2'){
            idRemoved = db.user[i].id;
            delete db.user[i];
            return `User with the id:${idRemoved} deleted`;
        }
    }
};

module.exports = {
    list,
    get,
    upsert,
    remove,
};

controller.js

const nanoid = require('nanoid');
// variable TABLE for user
const TABLA = 'user';

// function created to injec the store
module.exports = function(injectedStore) {
    // validating if store exist
    let store = injectedStore;
    if (!store) {
        store = require('../../../store/dummy');
    }
    // one validation is false use the function require from db
    function list() {
        return store.list(TABLA);
    }
    function get(id) {
        return store.get(TABLA, id);
    }

    function upsert(body) {
        console.log(body)
        const user = {
            name: body.name,
        }
        if (body.id) {
            user.id = body.id
        } else {
            user.id = nanoid()
        }

        return store.upsert(TABLA, user);
    }

    function remove(TABLA, id) {
        return store.remove(id);
    }


    return {
        list,
        get,
        upsert,
        remove,
    };
};

network.js

const express = require('express');

const response = require('../../../network/response');
const Controller = require('./index');

const router = express.Router();

router.get('/', function(req, res) {
    Controller.list()
        .then((lista) => {
            response.success(req, res, lista, 200);
        })
        .catch ((err) => {
            response.error(req, res, err.message, 500);
        });
})
router.get('/:id', function(req, res) {
    Controller.get(req.params.id)
        .then((user) => {
            response.success(req, res, user, 200);
        })
        .catch ((err) => {
            response.error(req, res, err.message, 500);
        });
});
router.post('/', function(req, res) {
    Controller.upsert(req.body)
        .then((user) => {
            response.success(req, res, user, 201);
        })
        .catch((err) => {
            response.error(req, res, err.message, 500);
        })
});

router.delete('/:id', function(req, res) {
    Controller.remove(req.params.id)
        .then((user) => {
            response.success(req, res, user, 200);
        })
        .catch((err) => {
            response.error(req, res, err.message, 500);
        });
});

Para usar nanoid tiene que bajarse a la version 2 para que les funcione. Y ademas, instalar body-parser para poder trabajar bien con el body.

npm i nanoid@^2.0.0
npm i body-parser

Espero que les sirva la info a los que van llegando

Los nombres de archivos me parecen confusos, asi como la estructura de las carpetas, quiza usando nombres mas descriptivos seria mejor

Mi código de user/network.js

// The express server
const { Router } = require('express');

// Required files for client responses and data controllers
const response = require('../../../network/responses');
const controller = require('./index');

// Instantiating express router
const router = Router();


// Set up the list of users
router.get('/', (req, res) => {
    controller.list()
        .then( list => {
            response.success( req, res, list, 200);
        })
        .catch( error => {
            response.error( req, res, error.message, 500);
        });
});

// Set up the user matching an id
router.get('/:id', (req, res) => {
    controller.get(req.params.id)
        .then( user => {
            response.success( req, res, user, 200);
        })
        .catch( error => {
            response.error( req, res, error.message, 500);
        });
});

module.exports = router;

Hay una parte que no le encuentro el sentido.
En el /api/user/index.js declaramos:

const store = require('../../../store/dummy');

Pero en /api/user/controller.js cuando recibimos como prop ese store. Lo evaluamos si viene o no vacío. Y en caso de estarlo volvemos a declarar:

  if (!store) {
    ++store = require('../../../store/dummy');++
  }

Entiendo que si en index.js ya recurrió a ‘…/…/…/store/dummy’ para importar los datos. Y han venido vacíos.
Por qué en el controller tras ver que estan vacíos volvemos a importar los datos del mismo fichero ‘…/…/…/store/dummy’.
¿Esto nos volverá a traer datos vacíos no? ¿O me he perdido en algún punto?

Saludos. 😄

Si no quieren pasar el valor de id a tipo string pueden usar `parseInt(req.params.id)` ```js router.get('/:id', async (req, res) => { try { const id = parseInt(req.params.id); const user = await Controller.get(id); response.success(req, res, user); } catch (error) { response.error(req, res, error.message); } }) ```
Buenas tardes, esta es mi solución **Forma para Eliminar:** <u>Archivo Network:</u> ```js router.get('/remove/:id', function (req, res){ Controller.remove(req.params.id) .then((user) => { response.success(req, res, user, 200); }) .catch((err) => { response.error(req, res, err.message, 500); }); }); ```<u>Archivo controller:</u> ```js function remove(id) { return store.remove(TABLA, id); } return { remove }; ```<u>Archivo dummy:</u> ```js async function remove(tabla, id) { db[tabla] = db[tabla].filter(function(diccionario) { return diccionario.id !== id; }); return true; } module.exports = { remove, }; ```**Forma para actualizar** <u>Archivo network:</u> ```js router.get('/upsert/:id/:nombre', function (req, res){ dictionary_user = req.params Controller.upsert(dictionary_user) .then((dictionary_user) => { response.success(req, res, dictionary_user, 200); }) .catch((err) => { response.error(req, res, err.message, 500); }); }); ```<u>Archivo Controller:</u> ```js function upsert(dictionary_user) { return store.upsert(TABLA, dictionary_user); } return { upsert }; ```<u>Archivo dummy:</u> ```js async function upsert(tabla, data) { db[tabla].push(data); return true; } module.exports = { upsert }; ```
Sin error: ![](https://static.platzi.com/media/user_upload/image-f81df89b-df09-4ab1-9418-683a12f0fb2a.jpg)
Error cuando Controller.list() ![]()![]()![](https://static.platzi.com/media/user_upload/image-790ea77e-ff79-4cfd-a80c-a39de4e3ef9b.jpg)

Por si alguien tuvo problemas para crear el inyector.

archivo users.injector.js

const srv = require('../services/users.service');
const store = require('../store/dummy');

//inyeccion de la dependencia store en el servicio.
const userServiceWithStore = srv(store);

module.exports = userServiceWithStore;

archivo users.service.js

const TABLA = 'user'; // tabla de la base de datos.

module.exports = (injectedStore) => {
  let store = injectedStore;
  if (!store) {
    const {
      list,
      get,
      upsert,
      remove
    } = require('../store/dummy');
  }

  // Obtener todos los usuarios.
  const listUsers = async () => {
    const users = await injectedStore.list(TABLA);
    return users;
  };

  return {
    listUsers
  };
};

sigo una estructura de routes ->controller ->service -> store para separar responsabilidades.

me ha desbaratado como venia trabajando en clases pasadas.

Yo lo hice así en mi index.js del controller para hacerlo un poco más reutilizable:

const { config } = require('../config/config')

let db = undefined;

// Verifica si hay una base de datos disponible para conectarse
if (config.dbAvailable === 'true') {
  db = require('../db/real');
} else {
  db = require('../db/dummy');
}

// validate
function notDB(db) {
  if (!db) {
    throw new Error('No access to DB');
  }
}
function notTab(tab) {
  if (!tab) {
    throw new Error('You need a table to connect');
  }
}
function notID(id) {
  if (!id) {
    throw new Error('You need id to get');
  }
}

// actions
async function getToDB(tab) {
  notDB(db)
  notTab(tab)

  const data = await db.list(tab)
  return data
}

async function getSomeFromDB(tab, id) {
  notDB(db)
  notTab(tab)
  notID(id)

  const user = await db.get(tab, id)
  return user
}

module.exports = {
  list: getToDB,
  get: getSomeFromDB,
};

mi solución es: network.js :

router.post("/add", async function (req, res) {
  try {
    const data = req.body;
    message = "add ok";
    await Controller.upsert(req.body);
    console.log(data);
    response.succes(req, res, message, 200);
  } catch (error) {
    message = "add error";
    response.error(req, res, error, message, 500);
    console.log(error);
  }
});

router.delete("/:id", function (req, res) {
  try {
    message = "delete ok";
    Controller.remove(req.params);
    response.succes(req, res, message, 200);
  } catch (error) {
    message = "delete error";
    response.error(req, res, error, message, 500);
    console.log(error);
  }
});

en controller.js :

function upsert(data) {
    return store.upsert(TABLA, data);
  }
  function remove(id) {
    return store.remove(TABLA, id);
  }
  return {
    list,
    get,
    upsert,
    remove,
  };

y en dummy.js:

async function upsert(tabla, data) {
  db[tabla].push(data);
  return data;
}
function remove(tabla, id) {
  const index = db[tabla].findIndex((item) => item.id == id);
  db[tabla].splice(index, 1);
  console.log("Entro en remove");
  return true;
}

module.exports = {
  list,
  get,
  upsert,
  remove,
};

En mi caso utilice async await y no promesas

Porque razon el parametro en el get de id, lleva dos puntos?
/:id, de otra forma no funciona

Mi solución al reto:
index.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

const config = require('./config.js');
const user = require('./api/components/user/network')

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// router
app.use('/api/user', user);

app.listen(config.api.port, () => {
    console.log('Api escuchando en el puerto: ', config.api.port)
})

network.js

const express = require('express');
const router = express.Router();
const response = require('../../../network/response');
const Controller = require('./index');

router.get('/', (req, res) => {
    Controller.list()
        .then(users => {
            response.success(req, res, users, 200);
        }).catch(err => {
            response.error(req, res, err.message, 500);
        })
})

router.get('/:id', (req, res) => {
    Controller.getUser(req.params.id)
        .then(user => {
            response.success(req, res, user, 200);
        }).catch(err => {
            response.error(req, res, err.message, 500);
        })
})

router.post('/', (req, res) => {
    Controller.addUser(req.body.user)
        .then(user => {
            response.success(req, res, user, 200);
        }).catch(err => {
            response.error(req, res, err.message, 500);
        })
})

router.delete('/:id', (req, res) => {
    Controller.deleteUser(req.params.id)
        .then(user => {
            response.success(req, res, user, 200);
        }).catch(err => {
            response.error(req, res, err.message, 500);
        })
})

module.exports = router;

controller.js

const TABLA = 'users';

module.exports = function(injectedStore) {
    let store = injectedStore;
    if (!store) {
        store = require('../../../store/dummy');
    }

    function list() {
        return store.list(TABLA);
    }

    function getUser(id) {
        return store.get(TABLA, id)
    }

    function addUser(user) {
        return store.upsert(TABLA, user);
    }

    function deleteUser(id){
        return store.remove(TABLA, id);
    }

    return {
        list,
        getUser,
        addUser,
        deleteUser,
    }
}

dummy.js

let db = {
    users: [
        {id: 1, nombre: "Luis Berenguer"},
        {id: 2, nombre: "Jorge Dominguez"}
    ]
}

async function list(tabla) {
    return db[tabla];
}

async function get(tabla, id) {
    let col = await list(tabla);
    return col.filter(item => item.id == id)[0] || null;
}

async function upsert(tabla, data) {
    await db[tabla].push(data);
    return data;
}

async function remove(tabla, id) {
    const index = db[tabla].findIndex(item => item.id == id);
    db[tabla].splice(index, 1);
}

module.exports = {
    list,
    get,
    upsert,
    remove
}

para no modificar el tipo de dato de numeric a string:

async function get (table, id) {
    console.log('I recibed table: ', table)
    console.log('I recibed id: ', id)

    let collection = await list (table);
    return collection.filter(item => item.id === parseInt(id))[0]||null;
}

Comparto mi codigo usando async await y arrow functions

dummy,js

const db = {
  user: [{ id: "1", name: "José" }],
};
const list = async (table) => db[table];
const get = async (table, id) => {
  let col = await list(table);
  return col.filter((item) => item.id === id[0] || null);
};
const upsert = async (table, data) => {
  db[collection].push(data);
};
const remove = async (table, id) => true;

module.exports = {
  list,
  get,
  upsert,
  remove,
};

network.js

const express = require("express");
const router = express.Router();
const response = require("../../../network/response");
const controller = require("./index");

router.get("/", async (req, res) => {
  try {
    const list = await controller.list();
    response.success(req, res, list, 200);
  } catch (error) {
    response.error(req, res, error.message, 500);
  }
});

router.get("/:id", async (req, res) => {
  try {
    const list = await controller.get(req.params.id);
    response.success(req, res, list, 200);
  } catch (error) {
    response.error(req, res, error.message, 500);
  }
});

module.exports = router;
const express = require('express');
const response = require('../../../network/response');
const controller = require('./index');
const router = express.Router();

router.get('/', function(req, res) {
    controller.listar()
        .then( data => {
            response.success(req, res, data, 200);
        })
        .catch( err => {
            response.error(req, res, err, 500);
        })
})

module.exports = router;

Que opina Carlos de utilizar Clases?
controller.js:

const TABLA = 'user';

module.exports = class UserController {

    constructor(store) {
        this.store = store || require('../../../store/dummy');
    }

    list() {
        return this.store.list(TABLA);
    }
}

index.js:

const store = require('../../../store/dummy');
const UserController = require('./controller');

module.exports = new UserController(store);

El controlador solo se crea una vez.

Esto responde a algún patron de diseño??

En la función remove consideré el caso en el que el id fuera inválido para que lanzara un error y posteriormente fuera recogido en el catch de network

async function remove(tabla, id) {
	let coleccion = await list(tabla);
	let idValid = await get(tabla, id);
	if (idValid) {
		coleccion.splice(coleccion.indexOf(idValid), 1);
		//return `Usuario con id: ${id} eliminado correctamente`;
		return coleccion
	}

	throw {
		status: 400,
		details: 'No se encontró el ID',
		message: 'Id inválido'
	};
}

me perdi un poco en la ultima parte, espero comprender mas en siguientes clases

router.post('/', (req, res) => {
    Controller.upsert(req.body)
        .then((user) => {
            response.success(req, res, user, 201);
        })
        .catch((err) => {
            response.error(req, res, err.message, 500);
        })
})

al colocar en el navegador http://localhost:3000/api/user/1

me trae la lista de los usuarios.!

Alguien sabe por que.?

En el caso de la funcion para eliminar un user, me di cuenta que si desde un async function se retorna un error, cuando estamos esperando la respuesta de esta funcion no se entrara al catch. Investigando un poco aprendi que se debe hacer un throw:

async function remove(table, id) {
    const indexToDelete = db[table].findIndex(item => item.id === id);
    if(indexToDelete === -1 ){
        throw new Error("No se encontro el usuario");
    }
    db[table].splice(indexToDelete, 1);
    return id;
}

Para mas info al respecto pueden consultar este articulo

😃

store

async function upsert(tabla, data) {
    
    db[tabla].push({
        id: db[tabla].length +1,
        name: data
    })
}

controller

function addOne(data) {
    return injectedStore.upsert(TABLA, data);
  }

network.controller

aggOne = (req, res)=>{
    const {name} = req.body
    addOne(name).then(()=>{
        success(res,'dato agregado')
    }).catch(e=> failure(res,e))
}

Decidí cambiar un poco la estructuración del controller usar clases de js e ir definiendo las funciones (list, get, upsert, etc) como métodos de la clase UserController. (esto implica que en el index.js de user se exporte el controller como new UserController(store) ). Dejo mi codigo

const TABLE = 'user';

class UserController {
    constructor(injectedStore) {
        this.store = injectedStore;
        if (!this.store) {
            this.store = require('../../../store/dummy');
        }
    }

    list() {
        return this.store.list(TABLE);
    }

    get(id) {
        return this.store.get(TABLE, id); 
    }
}

module.exports = UserController;

Ejemplo con async await (Simplemente lo hace mas bonito, mas legible que las promesas…)
El resto del codigo seria igual

router.get('/', async (req, res) => {
    try {
        const list = await controller.list();
        console.log(list);
        response.success(req, res, list, 200);
    } catch (error) {
        response.error(req, res, "Error getting data", 500, error);
    }
});

¡Hola a todos!

Comparto mi solución en typescript del archivo network.
también pueden dar un vistazo al repositorio y darme su opinión

github-repo

const newUser: RequestHandler = (req, res, next) => {
  const body = req.body;
  Controller.newOne(body)
    .then(() => {
      succes(req, res, 'Update ok', 201);
    })
    .catch((err) => {
      error(req, res, err, 500);
    });
};
const eraseUser: RequestHandler = (req, res, next) => {
  const id = req.params.id;
  Controller.deleteOne(id)
    .then(() => {
      succes(req, res, 'User deleted', 200);
    })
    .catch((err) => {
      error(req, res, err, 500);
    });
};

router.get('/', listAll);
router.get('/:id', oneUser);
router.post('/new', newUser);
router.delete('/:id', eraseUser);

He resuelto el reto. Me tardé mucho porque olvidé lo del body-parser y me salia que req.body era undefined

archivo principal

const express = require('express');

const config = require('../config.js');
const user = require('./components/user/network'); 
const bodyParser = require('body-parser');
const app = express();

//ROUTING
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/api/user', user);

app.listen(config.api.port, () => {
    console.log('API escuchando en el puerto ', config.api.port);
});

Dummy.js
En este caso agregué db[tabla].length + 1 para que el id se incrementara en 1 cada que agreguemos un usuario

const db = {
    'user': [
        { id: '1', name: 'Julio'},
    ]
};

async function list(tabla) {
    return db[tabla];
}

async function get(tabla, id) {
    let collection = await list(tabla);
    console.log(tabla);
    console.log(collection);
    return collection.filter( item => item.id === id)[0] || null;
}

async function upsert(tabla, data) {
    const newData = {
        id: db[tabla].length + 1,
        data: data.name,
    }
    db[tabla].push(newData);
    return newData;
}

async function remove(tabla, id) {
    return true;
}

module.exports = {
    list,
    get, 
    upsert,
    remove
}

network.js

router.post('/', (req, res) => {
    console.log(req.body);
    controller.insert(req.body)
        .then(user => {
            response.success(req, res, user, 201);
        })
        .catch((err) => {
            response.error(req, res, err.message, 500);
        })

});

controller.js

const TABLA = 'user';

module.exports = function (injectedStore) {
    let store = injectedStore;
    
    if (!store) {
        store = require('../../../store/dummy');
    }

    function list() {
        return store.list(TABLA);
    }
    
    function get(id) {
        return store.get(TABLA, id);
    }

    function insert(data) {
        if (!data.name) {
            return Promise.reject('No se encontro el valor de name');
        }

        const user = {
            name: data.name,
        }

        return store.upsert(TABLA, user);
    }

    return {
        list,
        get,
        insert,
    };
}

Llevo rato batallando con el ejercicio y solo tenía que agregar el body parser 😦

Voy a dejar la forma en como se puede solucionar el remove el upser es simplemente un push a si que no hay mucha ciencia

Creo que la mayoria va tratar de hacerlo con un bucle a si que se los voy a dejar en forma de bucle, pero tengan en cuenta que esto ocupa mucha memoria a si esto no es lo mas conveniente

async function remove(table, idDelete) {
  let data = [];
  let collection = await list(table);

  collection.map((element) => {
    if (element.id != idDelete) data.push(element)
  });

  db[table] = data;
  console.log(db)
  return `Element is delete`;

}

Ahora bien… la forma correcta a mi entender es con un findindex() para obtener el indice y ese borrarlo

async function remove(table, idDelete) {

  const ref = db[table].findIndex(item => item.id === idDelete)
  if (ref >= 0) db[table].splice(ref, 1)

  console.log(db)

  return `Element is delete`;

}

Ahora podemos usar Nullish operators

const store = injectedStore ?? require('@store/dummy')

Con ES2020

Que loco express no me todo get

Reto cumplido, en el index,js agrego esto para leer formatos json

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

en network.js creo las rutas para cada acción

router.post('/inset',function (req, res){
      controller.insert(req.body)
            .then((data)=>{
                  response.success(req, res, data, 200)
            }).catch((error) => {
                  response.error(req,res,error,500)
             })
})
router.patch('/update',function(req, res){
      controller.update(req.body)
            .then((data)=>{
                  response.success(req, res, data, 200)
            }).catch((error) => {
                  response.error(req,res,error,500)
            })
})
router.delete('/delete',function(req, res){
      controller.remove(req.body.id)
            .then((data)=>{
                  response.success(req, res, data, 200)
            }).catch((error) => {
                  response.error(req,res,error,500)
            })
})

en controller.js tambien puedo verificar la integridad de los datos y demas, de momento solo lo hice así

function insert(data){
            return store.insert(TABLE, data)
      }
 function update(data){
            return store.update(TABLE, data)
      }
 function remove(id){
            return store.remove(TABLE,id)
      }

por ultimo en el dummy.js hice varias cosas para simular las operaciones y de paso practicar JS

async function insert(table, data){
      return await db[table].push(data)
}

function update(table, data){
      return new Promise(async (resolve,reject)=> {
            let col = await list(table)
            col.filter(item =>{
                  if(item.id === data.id){
                        item.name = data.name
                        resolve(item)
                  }
            })
            reject('Algo salio mal')
      })
}

function remove(table, id){
      return new Promise(async (resolve,reject) =>{
            let col = await list(table)
            let cont = 0
            await col.filter(item => {
                  if(item.id===id){
                        col.splice(cont,1)
                        resolve(`usuario ${item.id} elimunado`)
                  }
                  cont++
            })
            reject('Error al eliminar al usuario')
      })
}
const db = {
    'user': [
        { id: '1', name: 'Carlos'},
        { id: '2', name: 'Fernando'},
    ],
};

async function list(tabla) {
    return db[tabla];
};
async function get(tabla, id) {
    let col = await list(tabla);
    return col.filter( item => item.id === id)[0] || null;
};

async function upsert(tabla, data) {

    console.log(data)
    db[tabla].push(data);
    return data
}

async function remove(id) {

    for(let i = 0; i < db.user.length; i++){
        if(db.user[i].id == '2'){
            idRemoved = db.user[i].id;
            delete db.user[i];
            return `User with the id:${idRemoved} deleted`;
        }
    }
};

module.exports = {
    list,
    get,
    upsert,
    remove,
};

Aquí empieza el cambio de las rutas a una promesa