Este es un proyecto personal de backend construido con Phoenix/Elixir. La aplicación está alojada en un servidor local haciendo uso de las siguientes herramientas:
- Ubuntu Linux: Sistema operativo del servidor (distribución Linux)
- CasaOS: Sistema de nube personal de código abierto que facilita la gestión de aplicaciones mediante Docker
- Docker: Plataforma de containerización que permite aislar y ejecutar aplicaciones en contenedores independientes
- Portainer: Herramienta de gestión visual para contenedores Docker, facilita la administración sin necesidad de comandos
- Túnel: Servicio de tunneling para exponer el servidor local a internet de forma segura
- Dominio personalizado: Nombre de dominio configurado para acceder a la API de manera profesional
- Ejecuta
mix setuppara instalar y configurar dependencias - Implementa tu base de datos en un nuevo archivo
.envy ejecutasource .env - Inicia el endpoint de Phoenix con:
mix phx.servero dentro de IEx:
iex -S mix phx.serverEndpoint: POST /api/register
URL: http://localhost:4000/api/register
Content-Type: application/json
{
"user": {
"name": "Test",
"last_name": "Test",
"email": "test@test.com",
"password": "password123",
"password_confirmation": "password123"
}
}{
"data": {
"id": 1,
"name": "Test",
"last_name": "Test",
"email": "test@test.com",
"token": "SFMyNTY.g3QAAAABbQAAAAVwY...",
"inserted_at": "2025-11-09T12:30:45Z",
"updated_at": "2025-11-09T12:30:45Z"
}
}{
"errors": {
"name": ["can't be blank"],
"email": ["has already been taken"]
}
}Endpoint: POST /api/login
URL: http://localhost:4000/api/login
Content-Type: application/json
{
"email": "test@test.com",
"password": "password123"
}{
"data": {
"id": 1,
"name": "Test",
"last_name": "Test",
"email": "test@test.com",
"token": "SFMyNTY.g3QAAAABbQAAAAVwY..."
}
}{
"error": "Contraseña inválida"
}{
"error": "Usuario no encontrado"
}POST /api/password/forgot
Body:
{
"email": "test@test.com"
}Respuesta exitosa:
{
"message": "Si el correo existe, se ha enviado un email con instrucciones"
}POST /api/password/reset
Body:
{
"token": "<TOKEN_DEL_EMAIL>",
"password": "nueva_password"
}Respuesta exitosa:
{
"message": "Contraseña actualizada exitosamente"
}POST /api/logout
Headers:
Authorization: Bearer <JWT_TOKEN>
Respuesta exitosa:
{
"message": "Sesión cerrada correctamente"
}POST /api/password/forgot
Body:
{
"email": "test@test.com"
}Respuesta exitosa:
{
"message": "Si el correo existe, se ha enviado un email con instrucciones"
}Revisar bandeja /dev/mailbox
URL del formulario /reset-password?token=8sFJiDHH0u-0eWirl9bt...
Mensaje de validación
GET /api/notes
Headers: Authorization: Bearer <JWT_TOKEN>
Respuesta:
[
{
"id": 1,
"title": "Nota 1",
"content": "Contenido de la nota",
"inserted_at": "2025-11-23T00:00:00Z"
}
]POST /api/notes
Headers: Authorization: Bearer <JWT_TOKEN>
Body:
{
"title": "Nota nueva",
"content": "Texto de la nota"
}Respuesta:
{
"id": 2,
"title": "Nota nueva",
"content": "Texto de la nota",
"inserted_at": "2025-11-23T00:00:00Z"
}GET /api/notes/:id
Headers: Authorization: Bearer <JWT_TOKEN>
PUT /api/notes/:id
Headers: Authorization: Bearer <JWT_TOKEN>
Body:
{
"title": "Nuevo título",
"content": "Nuevo contenido"
}DELETE /api/notes/:id
Headers: Authorization: Bearer <JWT_TOKEN>
Ejemplo de flujo completo (usando test@test.com)
- Registrar usuario con
/api/register. - Iniciar sesión con
/api/loginy guardar eltoken. - Usar el
tokenen el headerAuthorizationpara acceder a/api/notes(CRUD). - Para recuperar contraseña, usar
/api/password/forgoty seguir el link del email. - Para cerrar sesión, llamar a
/api/logoutcon el token.
- Todos los endpoints devuelven JSON.
- El token se debe enviar en el header
AuthorizationcomoBearer <token>para endpoints protegidos. - El sistema está listo para integración con frontend web/mobile.
- Emails de bienvenida y recuperación se envían usando Swoosh (en dev, revisa consola/logs).
| Campo | Validación | Error ejemplo |
|---|---|---|
| name | Requerido, 2–50 caracteres | "can't be blank", "should be 2-50 character(s)" |
| last_name | Requerido, 2–50 caracteres | "can't be blank", "should be 2-50 character(s)" |
| Requerido, formato válido, único | "can't be blank", "has invalid format", "has already been taken" | |
| password | Requerido, 8–100 caracteres | "can't be blank", "should be 8-100 character(s)" |
| password_confirmation | Debe coincidir con password | "does not match confirmation" |


