Este proyecto tiene como fin recrear el backend de una API con varias tablas de registros y relaciones de uno a muchos y viceversa entre ellas.
Esta API permite crear un usuario con el que reservar citas para tatuajes o compra de productos del mundo del tattoo. Las posibilidades que brinda a los usuarios son:
- Registro de un nuevo usuario y login previo a la realizaci贸n de cualquier acci贸n con la web.
- Consulta sin necesidad de cuenta de los servicios que 茅sta ofrece
- Modificaci贸n de datos del usuario.
- Creaci贸n de citas para la asistencia al negocio adem谩s de la especificaci贸n del fin de la misma de entre los 5 servicios ofrecidos
- Consulta de citas pendientes del usuario o de citas concretas mediante su ID.
- Modificaci贸n de citas creadas previamente a fin de elegir una nueva fecha o servicio determinados.
- Consulta de servicios ofrecidos
- Consulta de todas las citas de los usuarios (super_admin)
- Creacion y consulta de roles para los usuarios (super_admin)
- Creaci贸n de nuevos servicios para la web (super_admin)
Aqu铆 se encuentra enlace al repositorio del proyecto: https://github.com/MR-ant1/Tattoo-API.git
Seguir los pasos descritos a continuaci贸n para preparar todo el entorno de la API:
INSTRUCCIONES
- 1. Instalar Visual Studio Code, docker, alg煤n cliente y mysql workbrench en nuestro equipo. aqui dejo enlaces de descarga de docker y workbrench y un enlace a Postman, un ejemplo de cliente(tambi茅n podemos a帽adir "Thunder Client desde las extensiones de visual studio code RECOMENDADO): - Docker Desktop - Mysql workbrench - Postman - Visual studio Code-
- Abrimos windows powerShell e introducimos el siguiente comando para descargar la imagen de Mysql:
docker pull mysqlseguido de este otro comando para establecer un contenedor con esa imagen. Detras de name, daremos el nombre que queramos al contenedor, despues de -p, estableceremos los puertos que usaremos (siendo el de la derecha el de nuestro equipo) y en ROOT y 1234, introduciremos nuestro usuario y contrase帽a para este contenedor.
docker run --name mysql-container -p 3307:3306 -e MYSQL_ROOT_PASSWORD=1234 -d mysql-
- Crearemos una carpeta para el proyecto, la abriremos y ejecutaremos en consola el comando:
git: initUna vez lo hayamos hecho, Clonaremos el repositorio con el comando "git clone https://github.com/MR-ant1/Tattoo-API.git"
-
- Abrir terminal y ejecutar en orden de aparici贸n, los siguientes comandos:
npm init --ynpm install-
- Crear archivo ".env". Usar el sample incluido con las referencias necesarias para introducir nuestros datos de contenedor y poder levantar el servidor. Dejo un ejemplo de configuraci贸n:
PORT=4001
DB_USER=rooT
DB_PASSWORD=1234
DB_PORT=3306
DB_HOST=localhost
DB_DATABASE=TATTOO
JWT_SECRET=SECRETO-
- Crear base de datos en workbrench con el nombre igual al establecido en el archivo ".env" e importar la colecci贸n de endpoints a nuestro client. Esta se encuentra guardada en la carpeta HTTP
-
- Ejecutar migraciones mediante el comando:
typeorm-ts-node-commonjs migration:run -d ./src/database/db.ts脡sto enviar谩 a nuestra base de datos el formato de nuestras tablas y sus relaciones
-
- Ejecutamos los seeders mediante el comando:
ts-node ./src/database/seeders/seeder.tsCon este comando a帽adiremos la informaci贸n con los registros a nuestro mysql
-
- Levantamos servidor mediante el comando "npm run dev"
-
- Dirigirnos a nuestro client (thunderClient, insomnia, postman...) e importar el archivo de colecciones que incluye esta repositorio.
-
- Ya puedes probar las diferentes funciones del proyecto! mas abajo encontrar谩s toda la info sobre su funcionamiento.
En primer lugar, se llev贸 a cabo la creaci贸n de una variable "app" que relacionaremos con express para posibilitar el funcionamiento del servidor.
export const app: Application = express();
app.use(express.json());
Esta variable es definida y puesta en marcha justo debajo. Lo siguiente fue crear el archivo db.ts, donde definimos appDataSource, que contiene todos los datos asociados a nuestra base de datos y sus relaciones con migraciones y modelos
export const AppDataSource = new DataSource({
type: "mysql",
host: process.env.DB_HOST || "localhost",
port: Number(process.env.DB_PORT) || 3306,
username: process.env.DB_USER || "root",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_DATABASE || "test",
entities: [Role, User, Service, Appointment],
migrations: [Roles1708976565121, Users1708977244413, Services1708980299717, Appointments1708980851262],
synchronize: false,
logging: false,
})
Como se puede ver viene preparado para funcionar sin tener que cambiarle ningun dato, ya que cuenta con el v铆nculo con ".env" y, en caso contrario, cuenta con unos parametros por defecto igualmente validos.
Desde aqu铆, se pas贸 a la elaboraci贸n de la funci贸n para levantar el servidor.
Aqu铆 podemos ver como, importando la dependencia "dotenv", app-express y appDataSource mencionado arriba, ya pudimos definir la variable startServer, en la que inicializamos la base de datos.
Justo despues, la aplicaci贸n de express deja en "escucha" al servidor, por lo que ya puede empezar a procesar ordenes. Abajo ya fuera de funci贸n, invocamos startServer para poder iniciar base de datos y servidor unicamente ejecutando la ruta de este "server.ts" Se a帽ade nodemon a este comando para arrancar, de tal manera que 茅ste le permitir谩 reiniciarse cada vez que se realice un guardado.
MIGRACIONES
A continuaci贸n se ejemplifica uno de los cuatro archivos que contienen las migraciones:
En el resto de casos, la estructura es exactamente similar a esta. Se export贸 esta funci贸n que contiene el nombre de tabla y cada una de las columnas definidas para esta tabla servicios.
Estos documentos ser谩n los que tomar谩 como referencia nuestro mysql para elaborar las tablas de datos. Aqui decidiremos el tipo de dato que cada columna contendr谩, y algunas propiedades de ser necesario para estas columnas como el no poder estar vac铆a, o su tama帽o entre otras.
Adem谩s, indicaremos que columnas, si las hay, son foreign keys y van a tener relaci贸n con otras tablas, como se indica debajo:
foreignKeys: [
{
columnNames: ["user_id"],
referencedTableName: "users",
referencedColumnNames: ["id"],
onDelete: "CASCADE"
},
{
columnNames: ["service_id"],
referencedTableName: "services",
referencedColumnNames: ["id"],
onDelete: "CASCADE"
}]
(columna "user_id", que viene referenciada de la tabla users y apunta a la columna id. el "ON CASCADE" evita que podamos manipular tablas que guarden relaci贸n con esta foreign key aunque 茅sta no pertenezca como tal. Lo mismo con service_id justo debajo)
Despu茅s de establecer las migraciones, el siguiente paso es crear los modelos o entidades que conectan estas tablas con los controllers y endpoints que despu茅s definiremos. A continuaci贸n encontramos el modelo de usuarios que rige todas las interacciones que esta tendr谩 despu茅s con las dem谩s tablas. Definimos el nombre de la tabla junto a entity, para posteriormente ir incluyendo las columnas id como primary key (la que enlazar谩 con otras tablas) y las demas columnas secundarias.
Las dos 煤ltimas que se aprecian, son de los dos tipos de relaci贸n utilizados en este proyecto, onetoMany y ManyToOne, al ser role_id una foreign_key de roles, una tabla mas fuerte que Users. En caso contrario, tenemos appointments al ser Users mas fuerte y haber una columna "user_id" en appointments
Tanto migraciones como Entidades o modelos, deben ser referenciados en nuestro AppDataSOurce para que 茅ste cree el v铆nculo que nos permita llevar a cabo el siguiente paso. Muestro captura del mismo archivo donde se encontraban nuestro AppDataSource, pero ahora con todas las migraciones y modelos tanto importados al archivo, como introducidos en su apartado de AppDataSource.

MIDDLEWARES
Sirven para controlar el acceso de usuarios a distintas funciones, se crearon dos middlewares "isSuperAdmin" y "auth" encargados de dar acceso a las funciones super_admin y comprobar que el usuario ha hecho login respectivamente Las variables de ambos middlewares ser谩n llamadas en las rutas de los distintos endpoints de ser necesarios para limitar o verificar al usuario que la solicite.
AUTH

Se define la variable auth, que usar谩 los parametros req y res, y adem谩s NextFunction, que regula el paso a la siguiente funci贸n.
Despues ya dentro de funci贸n, definimos la variable token, que comprobar谩 si la cifra introducida es correcta eliminando mediante split las comillas que incluimos. Despu茅s utiliza el token y comprueba mediante la dependencia jwt, si el susodicho concuerda junto a la palabra secreta almacenada en .env .Si es, as铆 da paso a la ejecuci贸n de isSuperAdmin si est谩 presente, o a la variable del endpoint para que se ejecute.
IS_SUPER_ADMIN
Comprueba si el rolename asociado al user_id del token, es super_admin y da acceso al endpoint limitado a dicho rol.
ENDPOINTS
AUTH ENDPOINTS
- Registration:  No se muestra toda la funci贸n del controlador, pero en una primera parte, importamos Request y Response de express junto al modelo de User y definimos la funci贸n en la que pediremos los datos del nuevo usuario por el body. Una vez introducidos, se llevan acabo validaciones sobre el formato y el tama帽o de los datos y se trata la contrase帽a para encriptarla mediante bcrypt. Este endpoint sustituye en si mismo a la funci贸n de crear usuarios que a priori se pensaba incluir en "userControler" Para llevar a cabo este endpoint, iremos anuestro client y mediante el metodo POST, a帽adiremos la ruta asociada al registro: localhost:PORT/api/auth/register. Donde localhost se usa al ejecutarse en local, y PORT representa el puerto introducido en el archivo .env que ocupa la base de datos. Si importamos la colecci贸n que adjunto en la carpeta HTTP, deber铆an venir todo preparado y solo har谩 falta cambiar el puerto de ser distinto al que ahi vendr谩. Tras esto, iremos a la pesta帽a "Body", en introduciremos en el cuadro inferior de texto las 4 columnas a crear del usuario con sus valores donde aparecen las "x" tal y como vienen escritas aqui respetando comillas: ``` bash { "firstName": "xxxxx", "lastName": "xxxxxxx", "email": "xxxxxxxxx", "password": "xxxxxx" } ```- Login:
Con login volvemos a saltar la primera parte. Se aprecia arriba de la imagen como se define la funci贸n usando request y response, despu茅s se piden tanto email como contrase帽a por body y, tras dos validaciones, se pasa a la parte que se ilustra.
Se hace una b煤squeda de un solo usuario que tenga ese mismo email (no puede haber dos usuarios con un mismo email), y se obtienen sus datos mediante select.
Tras esto, se hace una comparaci贸n mediante bcrypt con la contrase帽a almacenada (este se encarga de desencriptarla) y por 煤ltimo, se lleva a cabo la creaci贸n de un token temporal para ese usuario con jwt, importado arriba del documento. Le indicamos aqui que contendr谩 tanto el user_id como el rol del usuario loggeado.Y en el archivo aparte "types>index",
export interface TokenData {
userId: number;
roleName: string;
};
declare global {
// Express
namespace Express {
export interface Request {
tokenData: TokenData;
}
}
}
damos formato a la funci贸n de token creada en el login. Este token ser谩 el que se use a partir de ahora para autentificar a cualquier usuario como perteneciente a la base de datos. Para hacer funcionar esta endpoint, debemos de nuevo acudir al body de nuestro client, y con mediante el metodo post y la ruta:
- localhost:PORT/api/auth/login client, y consultar algun correo de alg煤n usuario randomizado, aunque se recomienda usar el el correo con derechos de super_admin junto a la contrase帽a indicada(todos los usuarios randomizados y admin, tienen la misma contrase帽a por defecto)
"email": "superadmin@superadmin.com",
"password": "useruser"COPIAREMOS EL NUMERO DE TOKEN QUE LA CONSOLA DEL CLIENT DEVUELVA PARA, A PARTIR DE AHORA, UTILIZARLO EN NUESTRO CLIENT INTRODUCIENDOLO EN EL APARTADO AUTH>BEARER
ROLES ENDPOINTS
GET ROLES (super_admin): GET -> localhost:PORT/api/roles Obtendremos como super admins la posibilidad de consultar todos los roles disponibles para los usuarios. Por defecto: user, admin y super_adminCREATE ROLES (super_admin): POST -> localhost:PORT/api/roles

Podremos crear nuevos roles para la BD(base de datos), necesitaremos introducir la columna "name" con su valor en el body como veniamos haciendo anteriormente mas el token que guardamos al loggear
USER ENDPOINTS
GET USERS (super_admin): GET -> localhost:4001/api/users?limit=2&page=2 Este endpoint nos traer谩 a todos los usuarios. La ruta var铆a respecto a los demas dado que en este hemos a帽adido un limitador de usuarios por p谩gina a mostrar para evitar largas listas en casos de muchos registros. Se pueden manipular las cifras tras limit y page para modificar el numero de registros por pagina y la pagina en la que situarse. Puede quitarse la elecci贸n de pagina GET USERS BY ID (super_admin): GET -> localhost:PORT/api/users/id
Podremos obtener los datos de un usuario concreto. Pondremos el numero de de id del usuario en lugar del "id" de la ruta para indicar cual buscamos. CREATE USERS (super_admin): POST -> localhost:PORT/api/users A帽adir nuevos usuarios a la BD. Tendremos que introducir en el body de nuestro client, los siguientes registros con nuestra elecci贸n para cada uno. tambien el token en auth>bearer, como en todos los endpoints salvo login y register:
{
"firstName": "xxxxxxx",
"lastName": "xxxxxxxx",
"email": "xxxxxx@xxx.xxx",
"password": "xxxxxxx"
}GET PROFILE: GET -> localhost:PORT/api/users/profile
Obtener los datos de la propia cuenta logueada. Utilizar谩 el token asignado para identificar al due帽o de la petici贸n.
UPDATE PROFILE: PUT -> localhost:PORT/api/users/profile

De nuevo, mediante la identificaci贸n por token, obtendremos el usuario que realiza petici贸n y, mediante body, le introduciremos los registros y valores nuevos para nuestro usuario. Los introduciremos de la misma forma que explicamos en Register y en CREATE USERS.
SERVICES ENDPOINTS
GET SERVICES (open): GET localhost:PORT/api/services
Este es el 煤nico endpoint abierto a todo usuario incluso sin registro. Mostrar谩 los ervicios disponibles en el negocio. No precisa de token al no tener que autentificar
CREATE SERVICES (super_admin): POST localhost:PORT/api/services
APPOINTMENTS ENDPOINTS
GET MY APPOINTMENTS: GET localhost:PORT/api/appointments
Mediante la identificaci贸n por token, el sistema mostrar谩 todas las citas asocidas al usuario que lo solicita.

GET AN APPOINTMENT: GET localhost:PORT/api/appointments/id
Buscar una cita concreta mediante su n煤mero de id al final de la ruta superior (sustituyendo a "id")
CREATE APPOINTMENTS: POST localhost:PORT/api/appointments
Mediante la identificaci贸n por token, el sistema crear谩 una nueva cita para el usuario. Solo necesitara que introduzcamos por body del client los campos "appointmentDate" y "serviceId" como veniamos haciendo, para seleccionar la fecha y hora y el servicio que consumiremos. IMPORTANTE. introducir la fecha en formato YYYY-MM_DD HH-MM-SS
UPDATE APPOINTMENT: PUT localhost:PORT/api/appointments
Actualizar la hora o servicio seleccionado de una cita concreta. Introduciremos en el body los campos "id", "appointmentDate" y "serviceId" como en anteriores endpoints con sus nuevos valores.
-
Antonio Rodrigo - Full Stack Developer student
-
GitHub - Linkedin
-En el futuro se podr铆an implementar mas funciones como borrar usuarios, roles o citas. -Podr铆a mejorarse alg煤n endpoint a帽adiendole mas informaci贸n a devolver para mejorar la accesibilidad y manejo del programa.
Much铆simas gracias como siempre al equipo de GeeksHubs Achademy por brindarme esta posibilidad de desarrollarme en el mundo y a todos mis compa帽eros que siempre est谩n ahi para echar una mano cuando hace falta!
猬嗭笍 INDICE


