Job Board
Filtrador de ofertas de trabajo en informática usando la API de Jooble. Proyecto simple que me enseñó a integrar APIs externas y manejar variables de entorno.
Repositorio: https://github.com/mmoreno-byte/job-board
TL;DR
Backend Flask que consulta la API de Jooble, frontend HTML/CSS/JS vanilla. Aprendí a usar python-dotenv para gestionar API keys y a manejar respuestas de APIs externas.
API Jooble
Jooble es un agregador de ofertas de empleo. Su API permite buscar ofertas:
POST https://es.jooble.org/api/[API_KEY]
{
"keywords": "backend developer",
"location": "Sevilla",
"radius": 30
}Nota: la API funciona con datos de demostración sin API key real, pero tiene límites.
Backend: Flask
# backend/main.py
from flask import Flask, jsonify, request, render_template
from flask_cors import CORS
from dotenv import load_dotenv
import requests
import os
load_dotenv()
app = Flask(__name__)
CORS(app)
JOOBLE_API_KEY = os.getenv("JOOBLE_API_KEY", "demo")
JOOBLE_URL = "https://es.jooble.org/api/" + JOOBLE_API_KEY
@app.route("/")
def index():
return render_template("index.html")
@app.route("/api/jobs")
def search_jobs():
keywords = request.args.get("keywords", "")
location = request.args.get("location", "")
payload = {
"keywords": keywords,
"location": location,
"radius": 30
}
try:
response = requests.post(JOOBLE_URL, json=payload, timeout=10)
data = response.json()
jobs = data.get("jobs", [])
return jsonify({
"total": data.get("totalCount", 0),
"jobs": [{
"title": job.get("title"),
"company": job.get("company"),
"location": job.get("location"),
"snippet": job.get("snippet", "")[:200],
"link": job.get("link")
} for job in jobs]
})
except Exception as e:
return jsonify({"error": str(e), "jobs": []}), 500
if __name__ == "__main__":
app.run(debug=True)Variables de entorno con python-dotenv
# backend/.env (no hacer commit)
JOOBLE_API_KEY=tu_api_key_aquifrom dotenv import load_dotenv
import os
load_dotenv() # Carga .env automáticamente
api_key = os.getenv("JOOBLE_API_KEY")Nunca hagas commit de archivos
.env— añadirlos a.gitignore. Las API keys son secretos.
Frontend vanilla
// frontend/app.js
const searchJobs = async () => {
const keywords = document.getElementById('keywords').value;
const location = document.getElementById('location').value;
const response = await fetch(`/api/jobs?keywords=${keywords}&location=${location}`);
const data = await response.json();
const container = document.getElementById('results');
container.innerHTML = data.jobs.map(job => `
<div class="job-card">
<h3>${job.title}</h3>
<p><strong>${job.company}</strong> - ${job.location}</p>
<p>${job.snippet}</p>
<a href="${job.link}" target="_blank">Ver oferta</a>
</div>
`).join('');
};¿Por qué Flask en vez de FastAPI?
Para este proyecto, Flask fue suficiente:
- Rutas simples: solo unos pocos endpoints
- Sin validación compleja: la API de Jooble ya valida el payload
- Render template: Flask tiene renderizado de templates integrado, útil para prototipos
FastAPI habría sido mejor si:
- Necesitara validación de datos con Pydantic
- Quisiera autodocumentación con Swagger
- El proyecto fuera a escalar mucho
Estructura del proyecto
job-board/
├── backend/
│ ├── main.py
│ ├── requirements.txt
│ ├── .env.example # Template sin la key real
│ └── .env # (no commitear)
├── frontend/
│ ├── index.html
│ ├── styles.css
│ └── app.js
├── .gitignore
└── README.md.gitignore
# Backend
venv/
.env
# Frontend
node_modules/Lecciones aprendidas
python-dotenv es el estándar para vars de entorno local: carga el
.enval inicio y usaos.getenv()en vez de hardcodear.Las APIs externas pueden fallar: siempre envolver requests en try/except. Los timeout también son importantes.
Flask está bien para proyectos simples: no necesitas FastAPI para todo. Conoce las herramientas y usa la correcta.
Job Board me enseñó el flujo completo de integrar una API externa: desde el registro en Jooble hasta el deployment en Railway.
Más documentación en mmoreno.dev → Proyectos.