Framework Python para criação de APIs REST - Estilo Express.js
Project description
PYREST-FRAMEWORK v2.0.1 - Documentação Técnica
Visão Geral
O PYREST-FRAMEWORK é um framework Python moderno e robusto para criação de APIs REST, inspirado no Express.js do Node.js. Desenvolvido especificamente para projetos acadêmicos de Análise e Desenvolvimento de Sistemas (ADS), oferece uma experiência de desenvolvimento profissional com arquitetura MVC completa.
Características Principais
- ✅ Arquitetura MVC Completa: Controllers, Services, Models e Repositories
- ✅ Integração com Prisma ORM: Suporte a PostgreSQL, MySQL e SQLite
- ✅ Sistema de Validação Robusto: Validação automática de dados
- ✅ CLI Avançado: Criação automática de projetos com estrutura completa
- ✅ Middlewares Avançados: CORS, logging, auth, rate limiting
- ✅ Sintaxe Familiar: Inspirado no Express.js
- ✅ Documentação Extensa: Guias completos e exemplos
- ✅ Sistema de Testes: Cobertura abrangente
- ✅ Exemplos Práticos: Código real e funcional
Índice
- Instalação
- Primeiros Passos
- Conceitos Fundamentais
- Roteamento
- Request e Response
- Middlewares
- Tratamento de Erros
- CLI (Command Line Interface)
- Exemplos Avançados
- Testes
- Deploy
- Referência da API
Instalação
Requisitos do Sistema
- Python: 3.7 ou superior
- Sistema Operacional: Windows, macOS, Linux
- Memória: Mínimo 128MB RAM
- Espaço: Mínimo 50MB de espaço em disco
Instalação via pip
pip install pyrest-framework
Instalação em Desenvolvimento
git clone https://github.com/mamadusamadev/pyrest-framework.git
cd pyrest-framework
pip install -e .
Verificação da Instalação
# Testa a instalação
python install_and_test.py
# Ou usa o CLI
pyrest info
Primeiros Passos
Hello World
from pyrest import create_app
# Cria a aplicação
app = create_app()
# Define uma rota
@app.get('/')
def hello_world(req, res):
res.json({
"message": "Hello, PYREST-FRAMEWORK!",
"version": "2.0.1"
})
# Inicia o servidor
if __name__ == '__main__':
app.listen(port=3000, debug=True)
Usando o CLI
# Cria um novo projeto
pyrest create minha-api
# Entra no projeto
cd minha-api
# Inicia o servidor
pyrest serve --debug
# Ou inicia um servidor de exemplo
pyrest serve --quick
Conceitos Fundamentais
Arquitetura do Framework
O PYREST-FRAMEWORK segue uma arquitetura modular e extensível:
┌─────────────────┐
│ Application │ ← Classe principal
├─────────────────┤
│ Routes │ ← Sistema de roteamento
├─────────────────┤
│ Middlewares │ ← Camada de middlewares
├─────────────────┤
│ Request/Res │ ← Objetos HTTP
└─────────────────┘
Fluxo de Requisição
- Requisição HTTP chega ao servidor
- Middlewares são executados em sequência
- Roteamento encontra o handler correto
- Handler processa a requisição
- Response é enviada de volta
Componentes Principais
PyRestFramework (App)
- Classe principal do framework
- Gerencia rotas, middlewares e configurações
- Responsável pelo ciclo de vida da aplicação
Route
- Representa uma rota HTTP
- Suporte a parâmetros dinâmicos (
:id) - Matching baseado em regex
Request
- Objeto que representa a requisição HTTP
- Parsing automático de JSON e form data
- Acesso a headers, query params, body
Response
- Objeto que representa a resposta HTTP
- Múltiplos formatos de resposta (JSON, HTML, XML)
- Headers de segurança e cache
Roteamento
Métodos HTTP Suportados
@app.get('/users') # GET
@app.post('/users') # POST
@app.put('/users/:id') # PUT
@app.delete('/users/:id') # DELETE
@app.patch('/users/:id') # PATCH
@app.options('/users') # OPTIONS
Parâmetros de Rota
@app.get('/users/:id')
def get_user(req, res):
user_id = req.params['id'] # Acessa o parâmetro
res.json({"id": user_id})
# Requisição: GET /users/123
# Resultado: {"id": "123"}
Query Parameters
@app.get('/users')
def get_users(req, res):
page = req.get_query('page', '1')
limit = req.get_query('limit', '10')
res.json({
"page": page,
"limit": limit
})
# Requisição: GET /users?page=2&limit=20
Múltiplas Rotas
@app.get('/')
@app.get('/home')
def home(req, res):
res.json({"message": "Welcome!"})
Request e Response
Objeto Request
Propriedades Principais
req.method # Método HTTP (GET, POST, etc.)
req.path # Caminho da requisição
req.headers # Headers HTTP (dict)
req.body # Corpo da requisição (string)
req.params # Parâmetros de rota (dict)
req.query # Query parameters (dict)
req.json_data # Dados JSON parseados (dict)
req.form_data # Dados de formulário (dict)
Métodos Úteis
# Headers
user_agent = req.get_header('user-agent', 'Unknown')
# Query Parameters
page = req.get_query('page', '1')
# Route Parameters
user_id = req.get_param('id')
# JSON Data
data = req.get_json()
name = req.get_json('name', 'Default')
# Form Data
form_data = req.get_form()
email = req.get_form('email')
# Verificações
if req.is_json():
data = req.get_json()
if req.is_secure():
# Requisição HTTPS
Objeto Response
Métodos de Resposta
# JSON Response
res.json({"message": "Success"})
# Text Response
res.send("Hello World")
# HTML Response
res.html("<h1>Hello</h1>")
# XML Response
res.xml("<root><message>Hello</message></root>")
# File Response
res.file(content, "file.txt", "text/plain")
# Redirect
res.redirect('/new-page')
Headers e Status
# Status Code
res.status(201).json(data)
# Headers
res.header('Content-Type', 'application/json')
res.header('Authorization', 'Bearer token')
# Multiple Headers
res.headers_dict({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
})
Cookies e CORS
# Cookies
res.cookie('session', 'abc123', max_age=3600, secure=True)
res.clear_cookie('session')
# CORS
res.cors(origin='https://example.com', credentials=True)
Middlewares
Middlewares Incluídos
CORS (Cross-Origin Resource Sharing)
from pyrest import Middlewares
app.use(Middlewares.cors())
app.use(Middlewares.cors(
origin='https://example.com',
credentials=True
))
Logger
app.use(Middlewares.logger()) # combined
app.use(Middlewares.logger('dev')) # desenvolvimento
app.use(Middlewares.logger('common')) # comum
Body Parser
app.use(Middlewares.body_parser())
app.use(Middlewares.json_parser())
app.use(Middlewares.urlencoded())
Security Headers
app.use(Middlewares.security_headers())
Rate Limiting
app.use(Middlewares.rate_limit(
max_requests=100,
window_ms=60000
))
Authentication
app.use(Middlewares.auth_required())
Static Files
app.use(Middlewares.static_files('public', '/static'))
Middleware Personalizado
def custom_middleware(req, res):
# Adiciona timestamp à requisição
req.timestamp = time.time()
# Continua a execução
return True
app.use(custom_middleware)
Middleware que Para a Execução
def auth_middleware(req, res):
token = req.get_header('authorization')
if not token:
res.status(401).json({
"error": "Unauthorized"
})
return False # Para a execução
return True # Continua a execução
app.use(auth_middleware)
Tratamento de Erros
Handlers de Erro Personalizados
@app.error_handler(404)
def not_found(req, res):
res.json({
"error": "Not Found",
"message": f"Route '{req.path}' not found"
})
@app.error_handler(500)
def server_error(req, res):
res.json({
"error": "Internal Server Error",
"message": "Something went wrong"
})
Tratamento de Exceções
@app.get('/users/:id')
def get_user(req, res):
try:
user_id = int(req.params['id'])
user = find_user(user_id)
if not user:
res.status(404).json({
"error": "User not found"
})
return
res.json(user)
except ValueError:
res.status(400).json({
"error": "Invalid ID format"
})
except Exception as e:
res.status(500).json({
"error": "Internal server error",
"message": str(e)
})
Middleware de Tratamento Global
def error_handler_middleware(req, res):
try:
# Continua a execução
pass
except Exception as e:
res.status(500).json({
"error": "Internal Server Error",
"message": str(e)
})
return False
app.use(error_handler_middleware)
CLI (Command Line Interface)
Comandos Disponíveis
pyrest create <nome>
Cria um novo projeto com estrutura completa.
# Criação básica
pyrest create minha-api
# Com diretório específico
pyrest create minha-api -o /path/to/output
Estrutura criada:
minha-api/
├── app.py # Aplicação principal
├── README.md # Documentação
├── requirements.txt # Dependências
├── .gitignore # Git ignore
├── routes/ # Rotas organizadas
├── middlewares/ # Middlewares customizados
├── models/ # Modelos de dados
├── utils/ # Utilitários
└── tests/ # Testes
pyrest serve
Inicia um servidor PyRest.
# Carrega app.py (padrão)
pyrest serve
# Carrega arquivo específico
pyrest serve app.py
# Servidor de exemplo
pyrest serve --quick
# Configurações específicas
pyrest serve --port 8080 --host 0.0.0.0 --debug
pyrest info
Mostra informações sobre o framework.
pyrest info
Opções do Comando Serve
| Opção | Descrição | Padrão |
|---|---|---|
--port |
Porta do servidor | 3000 |
--host |
Host do servidor | localhost |
--debug |
Modo debug | False |
--quick |
Servidor de exemplo | False |
Exemplos Avançados
API REST Completa
from pyrest import create_app, Middlewares
app = create_app()
# Middlewares
app.use(Middlewares.cors())
app.use(Middlewares.logger('dev'))
app.use(Middlewares.json_parser())
# Simula banco de dados
users = []
# GET /users
@app.get('/users')
def get_users(req, res):
page = int(req.get_query('page', '1'))
limit = int(req.get_query('limit', '10'))
start = (page - 1) * limit
end = start + limit
paginated_users = users[start:end]
res.json({
"users": paginated_users,
"pagination": {
"page": page,
"limit": limit,
"total": len(users)
}
})
# POST /users
@app.post('/users')
def create_user(req, res):
data = req.json_data
if not data or 'name' not in data:
res.status(400).json({
"error": "Name is required"
})
return
new_user = {
"id": len(users) + 1,
"name": data['name'],
"email": data.get('email', '')
}
users.append(new_user)
res.status(201).json(new_user)
# GET /users/:id
@app.get('/users/:id')
def get_user(req, res):
try:
user_id = int(req.params['id'])
user = next((u for u in users if u['id'] == user_id), None)
if user:
res.json(user)
else:
res.status(404).json({
"error": "User not found"
})
except ValueError:
res.status(400).json({
"error": "Invalid ID"
})
# PUT /users/:id
@app.put('/users/:id')
def update_user(req, res):
try:
user_id = int(req.params['id'])
data = req.json_data
user_index = next((i for i, u in enumerate(users)
if u['id'] == user_id), None)
if user_index is None:
res.status(404).json({
"error": "User not found"
})
return
users[user_index].update(data)
users[user_index]['id'] = user_id
res.json(users[user_index])
except ValueError:
res.status(400).json({
"error": "Invalid ID"
})
# DELETE /users/:id
@app.delete('/users/:id')
def delete_user(req, res):
try:
user_id = int(req.params['id'])
user_index = next((i for i, u in enumerate(users)
if u['id'] == user_id), None)
if user_index is None:
res.status(404).json({
"error": "User not found"
})
return
deleted_user = users.pop(user_index)
res.json({
"message": "User deleted",
"user": deleted_user
})
except ValueError:
res.status(400).json({
"error": "Invalid ID"
})
if __name__ == '__main__':
app.listen(port=3000, debug=True)
Autenticação com JWT
import jwt
import hashlib
from datetime import datetime, timedelta
from pyrest import create_app, Middlewares
app = create_app()
# Configurações
JWT_SECRET = "your-secret-key"
JWT_EXPIRATION = 3600
# Middleware de autenticação
def auth_middleware(req, res):
# Rotas públicas
public_routes = ['/login', '/register']
if req.path in public_routes:
return True
# Verifica token
auth_header = req.get_header('authorization')
if not auth_header or not auth_header.startswith('Bearer '):
res.status(401).json({
"error": "Token required"
})
return False
token = auth_header[7:]
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])
req.user = payload
return True
except:
res.status(401).json({
"error": "Invalid token"
})
return False
app.use(auth_middleware)
# Login
@app.post('/login')
def login(req, res):
data = req.json_data
# Validação básica
if not data or 'username' not in data or 'password' not in data:
res.status(400).json({
"error": "Username and password required"
})
return
# Aqui você faria a validação real
if data['username'] == 'admin' and data['password'] == 'password':
payload = {
'user_id': 1,
'username': data['username'],
'exp': datetime.utcnow() + timedelta(seconds=JWT_EXPIRATION)
}
token = jwt.encode(payload, JWT_SECRET, algorithm='HS256')
res.json({
"message": "Login successful",
"token": token,
"expires_in": JWT_EXPIRATION
})
else:
res.status(401).json({
"error": "Invalid credentials"
})
# Rota protegida
@app.get('/profile')
def get_profile(req, res):
res.json({
"user_id": req.user['user_id'],
"username": req.user['username']
})
if __name__ == '__main__':
app.listen(port=3000, debug=True)
Testes
Executando Testes
# Todos os testes
pytest
# Com cobertura
pytest --cov=pyrest
# Testes específicos
pytest tests/test_core.py -v
# Com relatório HTML
pytest --cov=pyrest --cov-report=html
Escrevendo Testes
import unittest
from unittest.mock import Mock
from pyrest import create_app
class TestAPI(unittest.TestCase):
def setUp(self):
self.app = create_app()
def test_home_route(self):
# Simula requisição
req = Mock()
req.method = 'GET'
req.path = '/'
res = Mock()
res.json = Mock()
# Executa handler
@self.app.get('/')
def home(req, res):
res.json({"message": "Hello"})
# Verifica se rota foi registrada
self.assertEqual(len(self.app.routes), 1)
route = self.app.routes[0]
self.assertEqual(route.method, 'GET')
self.assertEqual(route.path, '/')
Testes de Integração
import requests
def test_api_integration():
# Inicia servidor em thread separada
import threading
import time
def start_server():
app.listen(port=3001)
server_thread = threading.Thread(target=start_server)
server_thread.daemon = True
server_thread.start()
time.sleep(1) # Aguarda servidor iniciar
# Testa API
response = requests.get('http://localhost:3001/')
assert response.status_code == 200
assert response.json()['message'] == 'Hello'
Deploy
Ambiente de Desenvolvimento
# Instalação
pip install -r requirements.txt
# Execução
python app.py
# Ou usando CLI
pyrest serve --debug
Ambiente de Produção
# Instalação
pip install pyrest-framework
# Execução
pyrest serve --host 0.0.0.0 --port 80
Docker
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 3000
CMD ["python", "app.py"]
Nginx (Proxy Reverso)
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Systemd Service
[Unit]
Description=PyRest API
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/api
ExecStart=/usr/bin/python3 app.py
Restart=always
[Install]
WantedBy=multi-user.target
Referência da API
Para documentação completa da API, consulte:
- API Reference - Referência detalhada de todas as classes e métodos
- Changelog - Histórico de versões e mudanças
Classes Principais
PyRestFramework
get(path, handler=None)- Define rota GETpost(path, handler=None)- Define rota POSTput(path, handler=None)- Define rota PUTdelete(path, handler=None)- Define rota DELETEpatch(path, handler=None)- Define rota PATCHoptions(path, handler=None)- Define rota OPTIONSuse(middleware)- Adiciona middlewareerror_handler(status_code)- Define handler de errolisten(port=3000, host='localhost', debug=False)- Inicia servidor
Request
get_header(name, default=None)- Obtém headerget_query(name, default=None)- Obtém query parameterget_param(name, default=None)- Obtém parâmetro de rotaget_json(key=None, default=None)- Obtém dados JSONget_form(key=None, default=None)- Obtém dados de formuláriois_json()- Verifica se é JSONis_form()- Verifica se é form datais_secure()- Verifica se é HTTPS
Response
status(code)- Define status codeheader(key, value)- Define headerjson(data, indent=None, ensure_ascii=False)- Resposta JSONsend(data)- Resposta textohtml(content)- Resposta HTMLxml(content)- Resposta XMLfile(content, filename, mimetype=None)- Resposta arquivoredirect(url, permanent=False)- Redirecionamentocookie(name, value, **options)- Define cookiecors(**options)- Headers CORS
Middlewares
cors(**options)- Middleware CORSlogger(format='combined')- Middleware de loggingbody_parser()- Middleware body parserjson_parser()- Middleware JSON parserurlencoded()- Middleware URL encodedstatic_files(directory, prefix='/static')- Middleware static filesrate_limit(max_requests=100, window_ms=60000)- Middleware rate limitingauth_required()- Middleware de autenticaçãoerror_handler()- Middleware de tratamento de errossecurity_headers()- Middleware de headers de segurança
Suporte e Comunidade
Recursos Adicionais
- GitHub: https://github.com/mamadusamadev/pyrest-framework
- Issues: Para reportar bugs ou solicitar features
- Discussions: Para dúvidas e discussões
- Wiki: Documentação adicional e tutoriais
Contribuindo
- Fork o projeto
- Crie uma branch para sua feature
- Commit suas mudanças
- Push para a branch
- Abra um Pull Request
Licença
Este projeto está licenciado sob a Licença MIT - veja o arquivo LICENSE para detalhes.
Conclusão
O PYREST-FRAMEWORK v2.0.1 oferece uma solução completa e profissional para desenvolvimento de APIs REST em Python. Com sua arquitetura MVC moderna, integração com Prisma ORM, sistema de validação robusto e CLI avançado, é ideal tanto para aprendizado quanto para projetos reais em produção.
Principais Melhorias da v2.0.1
- ✅ Arquitetura MVC Completa: Separação clara entre Controllers, Services, Models e Repositories
- ✅ Integração com Prisma: Suporte nativo a PostgreSQL, MySQL e SQLite
- ✅ Sistema de Validação: Validação automática de dados com múltiplos validadores
- ✅ CLI Avançado: Criação automática de projetos com estrutura profissional
- ✅ Fallback Inteligente: Funciona com ou sem banco de dados
- ✅ Documentação Completa: Guias detalhados para todas as funcionalidades
Para começar rapidamente:
# Instalação básica
pip install pyrest-framework
# Instalação com suporte a banco de dados
pip install pyrest-framework[database]
# Criação de projeto com estrutura MVC completa
pyrest create minha-api
# Execução
cd minha-api
pyrest serve --debug
Happy coding! 🚀
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyrest_framework-2.0.1.tar.gz.
File metadata
- Download URL: pyrest_framework-2.0.1.tar.gz
- Upload date:
- Size: 76.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe7ab8f363d3788033b96aab92bf9dc03a5d925dbbd9223ca3bace4eceb718ef
|
|
| MD5 |
521d02df12fca48a48ae531138738099
|
|
| BLAKE2b-256 |
fc91bc555ae291145dcbeed2c42b97c64ce7968b3702948edb32290983778c78
|
File details
Details for the file pyrest_framework-2.0.1-py3-none-any.whl.
File metadata
- Download URL: pyrest_framework-2.0.1-py3-none-any.whl
- Upload date:
- Size: 44.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0646b091b3f3f3c97ec25ee6b7e6f106cbb06e3f5d9650446695c0a7492ec13b
|
|
| MD5 |
3e54bf1dad280cf94923f8c2b6b85a26
|
|
| BLAKE2b-256 |
2cd5a116018ade05954c2ca41b753ac6637058ac7262390999b7ec6d285be123
|