Documentación completa de la Azure Serverless Product API v1.0
Production: https://fnapi6794.azurewebsites.net
Local Dev: http://localhost:7071
- Autenticación
- Formato de Respuestas
- Códigos de Estado HTTP
- Endpoints
- Modelos de Datos
- Códigos de Error
- Rate Limiting
- Ejemplos con Postman
Versión actual: No requiere autenticación (API pública)
⚠️ Nota de seguridad: En producción real, se recomienda implementar Azure AD B2C o API Keys.
Todas las respuestas de la API siguen un formato JSON estandarizado:
{
"success": true,
"data": { /* ... */ },
"message": "Operation completed successfully"
}{
"success": false,
"error": "Error message describing what went wrong",
"details": { /* optional additional info */ }
}| Código | Significado | Cuándo se usa |
|---|---|---|
200 |
OK | Operación exitosa (GET, PUT, DELETE) |
201 |
Created | Recurso creado exitosamente (POST) |
400 |
Bad Request | Datos de entrada inválidos |
404 |
Not Found | Recurso no encontrado |
422 |
Unprocessable Entity | Validación de Pydantic falló |
500 |
Internal Server Error | Error del servidor |
Verifica que la API esté funcionando correctamente.
GET /api/health{
"status": "healthy",
"timestamp": "2025-02-10T15:30:00.000Z",
"service": "Products API",
"version": "1.0.0"
}curl https://fnapi6794.azurewebsites.net/api/healthfetch('https://fnapi6794.azurewebsites.net/api/health')
.then(response => response.json())
.then(data => console.log(data));import requests
response = requests.get('https://fnapi6794.azurewebsites.net/api/health')
print(response.json())Obtiene todos los productos disponibles en el sistema.
GET /api/productsNo acepta parámetros por ahora. Filtros y paginación próximamente.
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Laptop Dell XPS 15",
"price": 1499.99,
"stock": 25,
"created_at": "2025-02-10T10:00:00.000Z",
"updated_at": "2025-02-10T10:00:00.000Z"
},
{
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"name": "Mouse Logitech MX Master 3",
"price": 99.99,
"stock": 150,
"created_at": "2025-02-10T11:00:00.000Z",
"updated_at": "2025-02-10T11:00:00.000Z"
}
],
"count": 2
}curl https://fnapi6794.azurewebsites.net/api/productsasync function getAllProducts() {
const response = await fetch('https://fnapi6794.azurewebsites.net/api/products');
const data = await response.json();
if (data.success) {
console.log(`Total products: ${data.count}`);
data.data.forEach(product => {
console.log(`${product.name} - $${product.price}`);
});
}
}import requests
response = requests.get('https://fnapi6794.azurewebsites.net/api/products')
data = response.json()
if data['success']:
for product in data['data']:
print(f"{product['name']} - ${product['price']}")Obtiene los detalles de un producto específico por su ID.
GET /api/products/{id}| Parámetro | Tipo | Descripción |
|---|---|---|
id |
UUID | Identificador único del producto |
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Laptop Dell XPS 15",
"price": 1499.99,
"stock": 25,
"created_at": "2025-02-10T10:00:00.000Z",
"updated_at": "2025-02-10T10:00:00.000Z"
}
}{
"success": false,
"error": "Product not found"
}curl https://fnapi6794.azurewebsites.net/api/products/550e8400-e29b-41d4-a716-446655440000async function getProduct(productId) {
const response = await fetch(
`https://fnapi6794.azurewebsites.net/api/products/${productId}`
);
const data = await response.json();
if (data.success) {
console.log(`Product: ${data.data.name}`);
console.log(`Price: $${data.data.price}`);
console.log(`Stock: ${data.data.stock} units`);
} else {
console.error(data.error);
}
}
// Uso
getProduct('550e8400-e29b-41d4-a716-446655440000');import requests
product_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://fnapi6794.azurewebsites.net/api/products/{product_id}'
)
data = response.json()
if response.status_code == 200:
product = data['data']
print(f"Product: {product['name']}")
print(f"Price: ${product['price']}")
elif response.status_code == 404:
print('Product not found')Crea un nuevo producto en el sistema.
POST /api/products
Content-Type: application/json{
"name": "Teclado Mecánico Keychron K2",
"price": 89.99,
"stock": 75
}| Campo | Tipo | Requerido | Validaciones |
|---|---|---|---|
name |
string | ✅ | Min: 3 chars, Max: 100 chars |
price |
float | ✅ | > 0 |
stock |
integer | ✅ | >= 0 |
{
"success": true,
"data": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"name": "Teclado Mecánico Keychron K2",
"price": 89.99,
"stock": 75,
"created_at": "2025-02-10T15:45:00.000Z",
"updated_at": "2025-02-10T15:45:00.000Z"
},
"message": "Product created successfully"
}{
"success": false,
"error": "Validation error",
"details": [
{
"field": "price",
"message": "ensure this value is greater than 0"
}
]
}curl -X POST https://fnapi6794.azurewebsites.net/api/products \
-H "Content-Type: application/json" \
-d '{
"name": "Teclado Mecánico Keychron K2",
"price": 89.99,
"stock": 75
}'async function createProduct(productData) {
const response = await fetch(
'https://fnapi6794.azurewebsites.net/api/products',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(productData)
}
);
const data = await response.json();
if (response.status === 201) {
console.log('Product created:', data.data);
return data.data;
} else {
console.error('Error:', data.error);
throw new Error(data.error);
}
}
// Uso
createProduct({
name: 'Teclado Mecánico Keychron K2',
price: 89.99,
stock: 75
});import requests
new_product = {
'name': 'Teclado Mecánico Keychron K2',
'price': 89.99,
'stock': 75
}
response = requests.post(
'https://fnapi6794.azurewebsites.net/api/products',
json=new_product
)
if response.status_code == 201:
product = response.json()['data']
print(f"Product created with ID: {product['id']}")
else:
print(f"Error: {response.json()['error']}")Actualiza los datos de un producto existente.
PUT /api/products/{id}
Content-Type: application/json| Parámetro | Tipo | Descripción |
|---|---|---|
id |
UUID | Identificador único del producto |
{
"name": "Teclado Mecánico Keychron K2 V2",
"price": 94.99,
"stock": 100
}📝 Nota: Puedes enviar solo los campos que deseas actualizar (actualización parcial).
| Campo | Tipo | Requerido | Validaciones |
|---|---|---|---|
name |
string | ❌ | Min: 3 chars, Max: 100 chars |
price |
float | ❌ | > 0 |
stock |
integer | ❌ | >= 0 |
{
"success": true,
"data": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"name": "Teclado Mecánico Keychron K2 V2",
"price": 94.99,
"stock": 100,
"created_at": "2025-02-10T15:45:00.000Z",
"updated_at": "2025-02-10T16:00:00.000Z"
},
"message": "Product updated successfully"
}{
"success": false,
"error": "Product not found"
}curl -X PUT https://fnapi6794.azurewebsites.net/api/products/7c9e6679-7425-40de-944b-e07fc1f90ae7 \
-H "Content-Type: application/json" \
-d '{
"name": "Teclado Mecánico Keychron K2 V2",
"price": 94.99,
"stock": 100
}'async function updateProduct(productId, updates) {
const response = await fetch(
`https://fnapi6794.azurewebsites.net/api/products/${productId}`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
}
);
const data = await response.json();
if (response.ok) {
console.log('Product updated:', data.data);
return data.data;
} else {
console.error('Error:', data.error);
throw new Error(data.error);
}
}
// Uso - Actualización parcial (solo precio)
updateProduct('7c9e6679-7425-40de-944b-e07fc1f90ae7', {
price: 84.99
});import requests
product_id = '7c9e6679-7425-40de-944b-e07fc1f90ae7'
updates = {
'price': 94.99,
'stock': 100
}
response = requests.put(
f'https://fnapi6794.azurewebsites.net/api/products/{product_id}',
json=updates
)
if response.status_code == 200:
product = response.json()['data']
print(f"Product updated: {product['name']}")
elif response.status_code == 404:
print('Product not found')Elimina un producto del sistema.
DELETE /api/products/{id}| Parámetro | Tipo | Descripción |
|---|---|---|
id |
UUID | Identificador único del producto |
{
"success": true,
"message": "Product deleted successfully"
}{
"success": false,
"error": "Product not found"
}curl -X DELETE https://fnapi6794.azurewebsites.net/api/products/7c9e6679-7425-40de-944b-e07fc1f90ae7async function deleteProduct(productId) {
const response = await fetch(
`https://fnapi6794.azurewebsites.net/api/products/${productId}`,
{
method: 'DELETE'
}
);
const data = await response.json();
if (response.ok) {
console.log('Product deleted successfully');
return true;
} else {
console.error('Error:', data.error);
return false;
}
}
// Uso
deleteProduct('7c9e6679-7425-40de-944b-e07fc1f90ae7');import requests
product_id = '7c9e6679-7425-40de-944b-e07fc1f90ae7'
response = requests.delete(
f'https://fnapi6794.azurewebsites.net/api/products/{product_id}'
)
if response.status_code == 200:
print('Product deleted successfully')
elif response.status_code == 404:
print('Product not found'){
"id": "string (UUID)",
"name": "string",
"price": "float",
"stock": "integer",
"created_at": "string (ISO 8601 datetime)",
"updated_at": "string (ISO 8601 datetime)"
}from pydantic import BaseModel, Field, validator
from typing import Optional
import uuid
from datetime import datetime
class ProductCreate(BaseModel):
name: str = Field(..., min_length=3, max_length=100)
price: float = Field(..., gt=0)
stock: int = Field(..., ge=0)
@validator('name')
def name_must_not_be_empty(cls, v):
if not v.strip():
raise ValueError('Name cannot be empty or whitespace')
return v.strip()
class Product(ProductCreate):
id: uuid.UUID
created_at: datetime
updated_at: datetime✅ Válido:
{
"name": "Laptop Gaming ASUS ROG",
"price": 1899.99,
"stock": 15
}❌ Inválido - Nombre muy corto:
{
"name": "PC",
"price": 999.99,
"stock": 5
}Error: name must have at least 3 characters
❌ Inválido - Precio negativo:
{
"name": "Monitor LG UltraWide",
"price": -299.99,
"stock": 20
}Error: price must be greater than 0
❌ Inválido - Stock negativo:
{
"name": "Webcam Logitech C920",
"price": 79.99,
"stock": -5
}Error: stock must be greater than or equal to 0
| Código | Error | Causa | Solución |
|---|---|---|---|
400 |
Bad Request | JSON malformado | Verifica el formato JSON |
404 |
Product not found | ID no existe | Verifica el ID del producto |
422 |
Validation error | Datos inválidos | Revisa las validaciones del modelo |
500 |
Internal server error | Error del servidor | Contacta soporte o revisa logs |
{
"success": false,
"error": "Validation error",
"details": [
{
"loc": ["body", "price"],
"msg": "ensure this value is greater than 0",
"type": "value_error.number.not_gt"
},
{
"loc": ["body", "name"],
"msg": "ensure this value has at least 3 characters",
"type": "value_error.any_str.min_length"
}
]
}Versión actual: No implementado
Próximamente:
- 100 requests/minuto por IP
- 1000 requests/día por IP
Descarga la colección completa: Azure-Serverless-API.postman_collection.json
-
Importar Colección:
- Abre Postman
- Click en "Import"
- Selecciona el archivo
.json
-
Configurar Variables:
- Variable:
base_url - Valor:
https://fnapi6794.azurewebsites.net
- Variable:
-
Ejecutar Tests:
- Selecciona "Run Collection"
- Verifica todos los endpoints
GET {{base_url}}/api/products
Headers:
Content-Type: application/json
Tests (JavaScript en Postman):
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response has success field", function () {
const jsonData = pm.response.json();
pm.expect(jsonData).to.have.property('success');
pm.expect(jsonData.success).to.be.true;
});
pm.test("Response has data array", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.data).to.be.an('array');
});// 1. Verificar que la API está activa
const health = await fetch('https://fnapi6794.azurewebsites.net/api/health');
console.log(await health.json());
// 2. Obtener todos los productos
const products = await fetch('https://fnapi6794.azurewebsites.net/api/products');
const allProducts = await products.json();
console.log(`Total productos: ${allProducts.count}`);
// 3. Crear un nuevo producto
const newProduct = await fetch('https://fnapi6794.azurewebsites.net/api/products', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Monitor Samsung 27"',
price: 299.99,
stock: 30
})
});
const created = await newProduct.json();
const productId = created.data.id;
console.log(`Producto creado con ID: ${productId}`);
// 4. Actualizar el stock
const updated = await fetch(`https://fnapi6794.azurewebsites.net/api/products/${productId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ stock: 25 })
});
console.log('Stock actualizado:', await updated.json());
// 5. Consultar el producto actualizado
const product = await fetch(`https://fnapi6794.azurewebsites.net/api/products/${productId}`);
console.log('Producto actual:', await product.json());
// 6. Eliminar el producto
const deleted = await fetch(`https://fnapi6794.azurewebsites.net/api/products/${productId}`, {
method: 'DELETE'
});
console.log('Producto eliminado:', await deleted.json());Problema: Access to fetch at '...' from origin '...' has been blocked by CORS policy
Solución:
az functionapp cors add \
--name fnapi6794 \
--resource-group rg-products-api \
--allowed-origins https://tu-frontend.azurewebsites.netProblema: La API retorna error 500
Solución:
- Revisar logs en Azure Portal
- Ejecutar localmente para debug:
cd backend func start
Problema: 404 - Function not found
Solución:
- Verificar deployment:
func azure functionapp list-functions fnapi6794
- Re-deploy si es necesario:
func azure functionapp publish fnapi6794 --python
- ✅ Initial release
- ✅ 6 endpoints CRUD completos
- ✅ Validación con Pydantic
- ✅ Documentación completa