🚀 Desarrollo de APIs (Application Programming Interface)

Guía Completa para Universitarios - Django + MySQL

Tema: Desarrollo de una interfaz de programación de aplicaciones (API)

📚 SABER - Dimensión Conceptual

Objetivo: Identificar el proceso de desarrollo de programación de aplicaciones (API)

🎯 1. ¿Qué es una API?

Definición Simple

Una API (Application Programming Interface) es como un "mesero en un restaurante":

  • Tú (Cliente): Haces un pedido (solicitud)
  • El Mesero (API): Lleva tu pedido a la cocina
  • La Cocina (Servidor/Base de datos): Prepara tu pedido
  • El Mesero (API): Te trae tu comida (respuesta)
Definición Técnica

Una API es un conjunto de reglas y protocolos que permite que diferentes aplicaciones se comuniquen entre sí. Actúa como intermediario que:

  • Recibe peticiones de clientes (navegadores, apps móviles, otras aplicaciones)
  • Procesa esas peticiones
  • Consulta o modifica datos en una base de datos
  • Devuelve respuestas en formato estandarizado (generalmente JSON)
Ejemplo Real: Cuando usas una aplicación del clima en tu celular:
  • La app NO tiene toda la información del clima
  • La app hace una solicitud a la API de un servicio meteorológico
  • La API consulta sus servidores y bases de datos
  • La API devuelve los datos del clima en formato JSON
  • Tu app muestra esa información de manera bonita

🔧 2. Tipos de APIs

REST API

La más común y la que usaremos

  • Usa HTTP/HTTPS
  • Sin estado (stateless)
  • Formato JSON
  • Fácil de usar
SOAP API

Más antigua y compleja

  • Usa XML
  • Más segura
  • Más rígida
  • Usada en banca
GraphQL

Moderna y flexible

  • Consultas personalizadas
  • Un solo endpoint
  • Creada por Facebook
  • Más compleja
WebSocket

Para tiempo real

  • Conexión bidireccional
  • Comunicación en vivo
  • Chats, juegos
  • Notificaciones

🌐 3. REST API - Conceptos Fundamentales

3.1 Métodos HTTP (Verbos)

Son las "acciones" que puedes realizar con una API:

GET
Obtener datos
POST
Crear datos
PUT
Actualizar datos
DELETE
Eliminar datos
Método Acción Ejemplo URL Resultado
GET Leer/Obtener GET /api/libros/ Lista todos los libros
GET Leer uno GET /api/libros/5/ Muestra el libro con ID 5
POST Crear POST /api/libros/ Crea un nuevo libro
PUT Actualizar PUT /api/libros/5/ Actualiza el libro ID 5
DELETE Eliminar DELETE /api/libros/5/ Elimina el libro ID 5

3.2 Códigos de Estado HTTP

Las APIs responden con códigos que indican el resultado de la operación:

Código Significado Cuándo se usa
200 OK Éxito La petición fue exitosa
201 Created Creado Se creó un nuevo recurso
204 No Content Sin contenido Éxito pero sin datos que devolver
400 Bad Request Petición incorrecta Datos enviados son inválidos
401 Unauthorized No autorizado Falta autenticación
403 Forbidden Prohibido No tienes permisos
404 Not Found No encontrado El recurso no existe
500 Internal Server Error Error del servidor Algo falló en el servidor

3.3 Formato JSON

Las APIs REST modernas usan JSON (JavaScript Object Notation) para enviar y recibir datos:

// Ejemplo de respuesta JSON de una API de libros { "id": 5, "titulo": "Cien Años de Soledad", "autor": "Gabriel García Márquez", "isbn": "978-0307474728", "precio": 299.99, "disponible": true, "fecha_publicacion": "1967-05-30" }
// Lista de libros (array JSON) [ { "id": 1, "titulo": "Don Quijote de la Mancha", "autor": "Miguel de Cervantes" }, { "id": 2, "titulo": "El Principito", "autor": "Antoine de Saint-Exupéry" } ]

🏗️ 4. Arquitectura de una Aplicación Web con API

Componentes Principales

Una aplicación web moderna orientada a servicios tiene estas capas:

  1. Frontend (Cliente)

    La interfaz que ve el usuario (HTML, CSS, JavaScript)

    • Navegador web
    • Aplicación móvil
    • Aplicación de escritorio
  2. API (Intermediario)

    El mesero que conecta el frontend con el backend (Django REST Framework)

    • Recibe peticiones HTTP
    • Valida datos
    • Procesa lógica de negocio
    • Devuelve respuestas JSON
  3. Backend (Servidor)

    La lógica de la aplicación (Django + Python)

    • Modelos de datos
    • Reglas de negocio
    • Autenticación y seguridad
  4. Base de Datos

    Donde se almacenan los datos (MySQL)

    • Tablas y relaciones
    • Persistencia de información
🔄 Flujo de una petición completa:
  1. Usuario hace clic en "Ver libros" en el navegador
  2. JavaScript envía petición GET a http://localhost:8000/api/libros/
  3. Django recibe la petición en su API
  4. Django consulta la base de datos MySQL
  5. MySQL devuelve los registros de libros
  6. Django convierte los datos a formato JSON
  7. La API envía la respuesta JSON al navegador
  8. JavaScript procesa el JSON y muestra los libros en HTML

⚙️ SABER HACER - Dimensión Actuacional

Objetivo: Desarrollar interfaces de programación de aplicaciones (API)

🛠️ 5. Desarrollo de API con Django REST Framework

5.1 ¿Por qué Django REST Framework?

Django REST Framework (DRF) es una biblioteca poderosa y flexible para construir APIs web con Django.

Ventajas:

  • ✅ Fácil de aprender y usar
  • ✅ Documentación excelente
  • ✅ Interfaz web navegable (puedes probar tu API desde el navegador)
  • ✅ Autenticación y permisos incorporados
  • ✅ Serialización automática de datos
  • ✅ Compatible con cualquier base de datos (MySQL, PostgreSQL, SQLite)

5.2 Componentes de Django REST Framework

1️⃣ Serializadores (Serializers)

Convierten objetos de Python/Django en JSON y viceversa

# libros/serializers.py from rest_framework import serializers from .models import Libro class LibroSerializer(serializers.ModelSerializer): """ Este serializador convierte los modelos Libro en JSON y valida los datos que llegan de las peticiones """ class Meta: model = Libro fields = ['id', 'titulo', 'autor', 'isbn', 'precio', 'fecha_publicacion', 'disponible'] # Campos opcionales de solo lectura read_only_fields = ['id'] def validate_precio(self, value): """Validación personalizada para el precio""" if value <= 0: raise serializers.ValidationError("El precio debe ser mayor a 0") return value def validate_isbn(self, value): """Validación del formato ISBN""" if len(value) < 10: raise serializers.ValidationError("ISBN debe tener al menos 10 caracteres") return value
2️⃣ Vistas (Views/ViewSets)

Manejan las peticiones HTTP y devuelven respuestas

# libros/views.py - Vista basada en funciones (Function-Based View) from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from .models import Libro from .serializers import LibroSerializer @api_view(['GET', 'POST']) def libro_lista(request): """ GET: Obtener lista de todos los libros POST: Crear un nuevo libro """ if request.method == 'GET': # Obtener todos los libros de la base de datos libros = Libro.objects.all() # Convertir a JSON (many=True porque son múltiples objetos) serializer = LibroSerializer(libros, many=True) # Devolver respuesta JSON return Response(serializer.data) elif request.method == 'POST': # Crear serializador con los datos recibidos serializer = LibroSerializer(data=request.data) # Validar los datos if serializer.is_valid(): # Guardar en la base de datos serializer.save() # Devolver el objeto creado con código 201 return Response(serializer.data, status=status.HTTP_201_CREATED) # Si hay errores de validación, devolverlos return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['GET', 'PUT', 'DELETE']) def libro_detalle(request, pk): """ GET: Obtener un libro específico PUT: Actualizar un libro DELETE: Eliminar un libro """ try: # Buscar el libro por ID (primary key) libro = Libro.objects.get(pk=pk) except Libro.DoesNotExist: # Si no existe, devolver error 404 return Response( {'error': 'Libro no encontrado'}, status=status.HTTP_404_NOT_FOUND ) if request.method == 'GET': # Serializar y devolver el libro serializer = LibroSerializer(libro) return Response(serializer.data) elif request.method == 'PUT': # Actualizar el libro con nuevos datos serializer = LibroSerializer(libro, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': # Eliminar el libro libro.delete() return Response(status=status.HTTP_204_NO_CONTENT)
3️⃣ URLs (Rutas)

Definen qué vista se ejecuta para cada URL

# libros/urls.py from django.urls import path from . import views urlpatterns = [ # GET /api/libros/ - Lista todos los libros # POST /api/libros/ - Crea un nuevo libro path('api/libros/', views.libro_lista, name='libro-lista'), # GET /api/libros/5/ - Obtiene el libro con ID 5 # PUT /api/libros/5/ - Actualiza el libro con ID 5 # DELETE /api/libros/5/ - Elimina el libro con ID 5 path('api/libros/<int:pk>/', views.libro_detalle, name='libro-detalle'), ]

5.3 ViewSets - Forma Avanzada

DRF ofrece ViewSets que simplifican aún más el código:

# libros/views.py - Usando ViewSets from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from .models import Libro from .serializers import LibroSerializer class LibroViewSet(viewsets.ModelViewSet): """ ViewSet que proporciona automáticamente: - list() para GET /api/libros/ - create() para POST /api/libros/ - retrieve() para GET /api/libros/{id}/ - update() para PUT /api/libros/{id}/ - destroy() para DELETE /api/libros/{id}/ """ queryset = Libro.objects.all() serializer_class = LibroSerializer # Acción personalizada: Obtener solo libros disponibles @action(detail=False, methods=['get']) def disponibles(self, request): """GET /api/libros/disponibles/""" libros_disponibles = Libro.objects.filter(disponible=True) serializer = self.get_serializer(libros_disponibles, many=True) return Response(serializer.data) # Acción personalizada para un libro específico @action(detail=True, methods=['post']) def marcar_no_disponible(self, request, pk=None): """POST /api/libros/{id}/marcar_no_disponible/""" libro = self.get_object() libro.disponible = False libro.save() return Response({'status': 'Libro marcado como no disponible'})
# libros/urls.py - URLs con ViewSets usando Routers from django.urls import path, include from rest_framework.routers import DefaultRouter from . import views # El Router genera automáticamente todas las URLs router = DefaultRouter() router.register(r'libros', views.LibroViewSet, basename='libro') urlpatterns = [ path('api/', include(router.urls)), ] # Esto genera automáticamente: # GET /api/libros/ - Lista # POST /api/libros/ - Crear # GET /api/libros/{id}/ - Detalle # PUT /api/libros/{id}/ - Actualizar # DELETE /api/libros/{id}/ - Eliminar # GET /api/libros/disponibles/ - Acción personalizada # POST /api/libros/{id}/marcar_no_disponible/ - Acción personalizada

🔗 6. Consumiendo la API desde el Frontend

6.1 Con JavaScript Fetch API

// Obtener todos los libros async function obtenerLibros() { try { const response = await fetch('http://localhost:8000/api/libros/'); if (!response.ok) { throw new Error('Error en la petición'); } const libros = await response.json(); console.log('Libros obtenidos:', libros); // Mostrar en la página mostrarLibros(libros); } catch (error) { console.error('Error:', error); } } // Crear un nuevo libro async function crearLibro(libroData) { try { const response = await fetch('http://localhost:8000/api/libros/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(libroData) }); if (!response.ok) { const errors = await response.json(); console.error('Errores de validación:', errors); return; } const nuevoLibro = await response.json(); console.log('Libro creado:', nuevoLibro); // Actualizar la lista obtenerLibros(); } catch (error) { console.error('Error:', error); } } // Actualizar un libro async function actualizarLibro(id, libroData) { try { const response = await fetch(`http://localhost:8000/api/libros/${id}/`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(libroData) }); if (response.ok) { const libroActualizado = await response.json(); console.log('Libro actualizado:', libroActualizado); obtenerLibros(); } } catch (error) { console.error('Error:', error); } } // Eliminar un libro async function eliminarLibro(id) { try { const response = await fetch(`http://localhost:8000/api/libros/${id}/`, { method: 'DELETE' }); if (response.ok) { console.log('Libro eliminado'); obtenerLibros(); } } catch (error) { console.error('Error:', error); } } // Ejemplo de uso const nuevoLibro = { titulo: "Cien Años de Soledad", autor: "Gabriel García Márquez", isbn: "978-0307474728", precio: 299.99, fecha_publicacion: "1967-05-30", disponible: true }; crearLibro(nuevoLibro);

6.2 Con Axios (Alternativa más moderna)

// Primero instalar: npm install axios import axios from 'axios'; // Configurar base URL const api = axios.create({ baseURL: 'http://localhost:8000/api', headers: { 'Content-Type': 'application/json' } }); // Obtener libros const obtenerLibros = async () => { try { const response = await api.get('/libros/'); console.log(response.data); } catch (error) { console.error(error.response.data); } }; // Crear libro const crearLibro = async (libro) => { try { const response = await api.post('/libros/', libro); console.log('Creado:', response.data); } catch (error) { console.error('Error:', error.response.data); } };

🔒 7. Seguridad en APIs

7.1 CORS (Cross-Origin Resource Sharing)

¿Qué es CORS?

Es una medida de seguridad del navegador que evita que sitios web maliciosos accedan a tu API.

En nuestra práctica, necesitamos configurar CORS porque el frontend y backend corren en diferentes puertos.

# settings.py # Ya instalamos: pip install django-cors-headers INSTALLED_APPS = [ ... 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # Debe estar al inicio 'django.middleware.common.CommonMiddleware', ... ] # Permitir peticiones desde estos orígenes CORS_ALLOWED_ORIGINS = [ "http://localhost:3000", # React "http://localhost:8080", # Vue "http://127.0.0.1:5500", # Live Server ] # O en desarrollo puedes permitir todo (NO EN PRODUCCIÓN) CORS_ALLOW_ALL_ORIGINS = True

7.2 Autenticación

DRF ofrece varios métodos de autenticación:

# settings.py REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticatedOrReadOnly', ], }
# views.py - Aplicar permisos a una vista from rest_framework.permissions import IsAuthenticated, IsAdminUser class LibroViewSet(viewsets.ModelViewSet): queryset = Libro.objects.all() serializer_class = LibroSerializer def get_permissions(self): """Permisos según la acción""" if self.action in ['create', 'update', 'destroy']: # Solo administradores pueden crear/editar/eliminar permission_classes = [IsAdminUser] else: # Cualquiera puede ver permission_classes = [IsAuthenticated] return [permission() for permission in permission_classes]

📊 8. Paginación y Filtros

8.1 Paginación

Cuando tienes muchos datos, es mejor enviarlos en páginas:

# settings.py REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 # 10 libros por página }
// Respuesta JSON con paginación { "count": 47, // Total de libros "next": "http://localhost:8000/api/libros/?page=2", "previous": null, "results": [ {"id": 1, "titulo": "..."}, // ... 10 libros ] }

8.2 Filtros y Búsqueda

# Instalar: pip install django-filter # settings.py INSTALLED_APPS = [ ... 'django_filters', ] REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.SearchFilter', 'rest_framework.filters.OrderingFilter', ], }
# views.py class LibroViewSet(viewsets.ModelViewSet): queryset = Libro.objects.all() serializer_class = LibroSerializer # Filtrar por estos campos filterset_fields = ['autor', 'disponible'] # Buscar en estos campos search_fields = ['titulo', 'autor', 'isbn'] # Ordenar por estos campos ordering_fields = ['titulo', 'precio', 'fecha_publicacion'] ordering = ['titulo'] # Orden por defecto
Ejemplos de URLs con filtros:
  • /api/libros/?autor=García Márquez - Filtrar por autor
  • /api/libros/?disponible=true - Solo disponibles
  • /api/libros/?search=quijote - Buscar "quijote"
  • /api/libros/?ordering=-precio - Ordenar por precio descendente
  • /api/libros/?search=python&disponible=true&ordering=titulo - Combinar filtros

🧪 9. Probando la API

9.1 Navegador Web

¡DRF tiene una interfaz web increíble!

Simplemente visita las URLs en tu navegador:

  • http://localhost:8000/api/libros/
  • Verás una interfaz bonita donde puedes:
    • Ver los datos en formato JSON
    • Crear nuevos registros con formularios
    • Editar y eliminar desde el navegador

9.2 Postman o Insomnia

Herramientas profesionales para probar APIs:

  1. Descargar Postman: postman.com
  2. Crear una nueva petición
  3. Seleccionar método (GET, POST, PUT, DELETE)
  4. Ingresar URL: http://localhost:8000/api/libros/
  5. Para POST/PUT, agregar JSON en el Body
  6. Enviar y ver la respuesta

9.3 Con cURL (Línea de comandos)

# GET - Obtener todos los libros curl http://localhost:8000/api/libros/ # POST - Crear un libro curl -X POST http://localhost:8000/api/libros/ \ -H "Content-Type: application/json" \ -d '{ "titulo": "El Principito", "autor": "Antoine de Saint-Exupéry", "isbn": "978-0156012195", "precio": 199.99, "fecha_publicacion": "1943-04-06", "disponible": true }' # PUT - Actualizar libro curl -X PUT http://localhost:8000/api/libros/1/ \ -H "Content-Type: application/json" \ -d '{"precio": 249.99}' # DELETE - Eliminar libro curl -X DELETE http://localhost:8000/api/libros/1/

🤝 SABER SER-CONVIVIR - Dimensión Socioafectiva

Competencias profesionales y trabajo en equipo

💼 10. Buenas Prácticas en el Desarrollo de APIs

10.1 Nomenclatura y Estructura

✅ URLs RESTful correctas
  • Usa sustantivos en plural: /api/libros/ no /api/libro/
  • Usa guiones, no guiones bajos: /api/libros-populares/
  • Minúsculas siempre: /api/libros/ no /api/Libros/
  • Sin verbos en las URLs: POST /api/libros/ no /api/crear-libro/
✅ Versionado de APIs

Las APIs evolucionan, usa versiones para no romper aplicaciones existentes:

  • /api/v1/libros/ - Versión 1
  • /api/v2/libros/ - Versión 2 (cuando haces cambios importantes)

10.2 Manejo de Errores

# Respuestas de error consistentes { "error": { "code": "LIBRO_NOT_FOUND", "message": "El libro con ID 999 no existe", "details": { "id": 999, "timestamp": "2024-01-15T10:30:00Z" } } }

10.3 Documentación

Documenta tu API

Usa herramientas como Swagger/OpenAPI para generar documentación automática:

# Instalar drf-spectacular pip install drf-spectacular # settings.py INSTALLED_APPS = [ ... 'drf_spectacular', ] REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', } # urls.py from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView urlpatterns = [ path('api/schema/', SpectacularAPIView.as_view(), name='schema'), path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema')), ] # Ahora visita: http://localhost:8000/api/docs/

10.4 Testing

Las APIs profesionales tienen pruebas automatizadas:

# libros/tests.py from rest_framework.test import APITestCase from rest_framework import status from .models import Libro class LibroAPITestCase(APITestCase): def setUp(self): """Crear datos de prueba""" self.libro = Libro.objects.create( titulo="Test Libro", autor="Test Autor", isbn="1234567890", precio=100.00 ) def test_get_libros(self): """Probar que se pueden obtener los libros""" response = self.client.get('/api/libros/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) def test_create_libro(self): """Probar crear un libro""" data = { 'titulo': 'Nuevo Libro', 'autor': 'Nuevo Autor', 'isbn': '0987654321', 'precio': 150.00 } response = self.client.post('/api/libros/', data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Libro.objects.count(), 2) # Ejecutar: python manage.py test

👥 11. Trabajo en Equipo y Comunicación

Roles en un equipo de desarrollo con APIs
  • Backend Developer: Desarrolla la API y la lógica del servidor
  • Frontend Developer: Consume la API y crea la interfaz de usuario
  • DevOps: Despliega y mantiene la infraestructura
  • QA Tester: Prueba la API y reporta bugs
Comunicación efectiva:
  • 📝 Documenta cada endpoint de tu API
  • 💬 Comunica cambios en la API al equipo frontend
  • 🐛 Reporta errores de manera clara y con ejemplos
  • 🤝 Colabora en la definición de contratos de API
  • 📊 Usa herramientas como Postman Collections para compartir ejemplos

🎓 12. Resumen y Próximos Pasos

Has aprendido:
  • ✅ Qué es una API y por qué es importante
  • ✅ Diferencias entre REST, SOAP, GraphQL y WebSocket
  • ✅ Métodos HTTP (GET, POST, PUT, DELETE)
  • ✅ Códigos de estado HTTP
  • ✅ Formato JSON
  • ✅ Arquitectura de aplicaciones orientadas a servicios
  • ✅ Django REST Framework y sus componentes
  • ✅ Serializers, Views, ViewSets y URLs
  • ✅ Cómo consumir APIs desde JavaScript
  • ✅ Seguridad (CORS, Autenticación)
  • ✅ Paginación y filtros
  • ✅ Testing y documentación
  • ✅ Buenas prácticas profesionales
🚀 Próximos pasos en tu aprendizaje:
  1. Practica creando APIs para diferentes dominios (tareas, usuarios, productos)
  2. Aprende sobre autenticación JWT (JSON Web Tokens)
  3. Explora WebSockets para comunicación en tiempo real
  4. Estudia GraphQL como alternativa a REST
  5. Aprende sobre microservicios y arquitecturas distribuidas
  6. Practica con bases de datos NoSQL (MongoDB)
  7. Implementa caching con Redis
  8. Aprende sobre Docker y contenedores
  9. Estudia CI/CD para despliegues automatizados
⚠️ Recuerda:
  • Una buena API es intuitiva y fácil de usar
  • La documentación es tan importante como el código
  • Siempre valida los datos de entrada
  • Implementa manejo de errores robusto
  • Piensa en la escalabilidad desde el inicio
  • La seguridad no es opcional

📚 Recursos Adicionales

Recurso Descripción URL
Django REST Framework Documentación oficial django-rest-framework.org
MDN Web Docs Referencia de HTTP y APIs developer.mozilla.org
Postman Learning Tutoriales de APIs learning.postman.com
REST API Tutorial Guía completa de REST restfulapi.net
Real Python Tutoriales de Django y DRF realpython.com