<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Programación &#8211; Jeff Santillán</title>
	<atom:link href="https://jeffsantillan.com/blog/category/programacion/feed/" rel="self" type="application/rss+xml" />
	<link>https://jeffsantillan.com/blog</link>
	<description>Transformación Digital y Programación</description>
	<lastBuildDate>Fri, 19 Jun 2026 05:27:18 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>
	<item>
		<title>Nestjs manejo global de errores con Exception Filters</title>
		<link>https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 05:06:50 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[REST API]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=300</guid>

					<description><![CDATA[NestJS por defecto devuelve errores genéricos como 500 Internal Server Error sin contexto. Un Exception Filter &#8230; <a title="Nestjs manejo global de errores con Exception Filters" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/"><span class="screen-reader-text">Nestjs manejo global de errores con Exception Filters</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">NestJS por defecto devuelve errores genéricos como <code>500 Internal Server Error</code> sin contexto. Un Exception Filter intercepta TODOS los errores de la app en un solo lugar antes de que lleguen al cliente, permitiéndote estandarizar las respuestas.</p>



<p class="wp-block-paragraph">Tenemos que crear un archivo llamado src/filter/http-exception.filter.ts</p>



<pre class="wp-block-code has-small-font-size"><code>import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Prisma } from '../../generated/prisma/client';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse&lt;Response&gt;();
    const request = ctx.getRequest&lt;Request&gt;();

    let status = HttpStatus.INTERNAL_SERVER_ERROR;
    let message: string | string&#91;] = 'Error interno del servidor';

    // Error de NestJS (404, 400, etc.)
    if (exception instanceof HttpException) {
      status = exception.getStatus();
      const exceptionResponse = exception.getResponse();
      message =
        typeof exceptionResponse === 'object'
          ? (exceptionResponse as any).message
          : exceptionResponse;
    }

    // Error de Prisma — constraint unique (email o dni duplicado)
    else if (exception instanceof Prisma.PrismaClientKnownRequestError) {
      if (exception.code === 'P2002') {
        status = HttpStatus.CONFLICT; // 409
        const fields = (exception.meta?.target as string&#91;])?.join(', ');
        message = `Ya existe un registro con ese ${fields}`;
      }
      // Error de FK — registro relacionado no existe
      else if (exception.code === 'P2003') {
        status = HttpStatus.BAD_REQUEST; // 400
        message = 'El registro relacionado no existe';
      }
      // Registro no encontrado al hacer update/delete
      else if (exception.code === 'P2025') {
        status = HttpStatus.NOT_FOUND; // 404
        message = 'Registro no encontrado';
      }
    }

    response.status(status).json({
      success: false,
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}</code></pre>



<p class="wp-block-paragraph">@catch = Captura cualquier excepcion<br>@catch(HttpException) = Captura solo errores HTTP.<br>ArgumentHost = Es una bastraccion que permite acceder al request/response independientemente del protocolo (HTTP, WebSocket, microservicios)</p>



<p class="wp-block-paragraph">Luego registrarlo globalmente en el main.ts. importando el mismo archivo creado y registrándolo de manera global.</p>



<pre class="wp-block-code"><code>...
<strong>import { AllExceptionsFilter } from './filters/http-exception.filter';</strong>

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Exception Filter global
  <strong>app.useGlobalFilters(new AllExceptionsFilter());</strong>

...
}</code></pre>



<p class="wp-block-paragraph">Aunque existen niveles de aplicación como a un método o a un controlador (todos los endpoints) con @UseFilters() y toda la aplicación con <strong>app.useGlobalFilters</strong>()</p>



<p class="wp-block-paragraph">Antes el error genérico era algo como:</p>



<pre class="wp-block-code"><code>{ "statusCode": 500, "message": "Internal server error" }</code></pre>



<p class="wp-block-paragraph">Ahora seria algo asi</p>



<pre class="wp-block-code"><code>{
  "success": false,
  "statusCode": 409,
  "message": "Ya existe un registro con ese email",
  "timestamp": "2026-06-19T03:00:00.000Z",
  "path": "/docente"
}</code></pre>



<p class="wp-block-paragraph">Eso seria, podemos gestionar diferentes errores de manera centralizada.</p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs uso de Swagger para documentación</title>
		<link>https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 04:34:51 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[nestjs]]></category>
		<category><![CDATA[swagger]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=296</guid>

					<description><![CDATA[Swagger es una herramienta que genera documentación interactiva de tu API automáticamente. En lugar de documentar &#8230; <a title="Nestjs uso de Swagger para documentación" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/"><span class="screen-reader-text">Nestjs uso de Swagger para documentación</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Swagger es una herramienta que genera documentación interactiva de tu API automáticamente. En lugar de documentar manualmente cada endpoint en un Word o Notion, Swagger lee tu código y genera una interfaz web donde cualquier developer puede ver y probar la API sin usar Postman o PowerShell</p>



<p class="wp-block-paragraph">Extendiendo el proyecto de Usuario: <a href="https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/" data-type="link" data-id="https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/">Enlace</a></p>



<p class="wp-block-paragraph"><strong>Podemos incluirle el Swagger para documentar la API</strong>.</p>



<p class="wp-block-paragraph">instalar</p>



<pre class="wp-block-code"><code>npm install @nestjs/swagger</code></pre>



<p class="wp-block-paragraph">Es asi que al archivo de main.ts le voy a incluir o habilitar Swagger, hay que importar en el main y agregar las siguientes lineas</p>



<pre class="wp-block-code has-small-font-size"><code>import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

// Swagger
  const config = new DocumentBuilder()
      .setTitle('Sistema de Usuario API')
      .setDescription('API para gestion de usuarios')
      .setVersion('1.0')
      .addTag('usuario')
      .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);
</code></pre>



<p class="wp-block-paragraph">Quedando el archivo main.ts de la siguiente manera.</p>



<pre class="wp-block-code"><code>import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common'; // Validator
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // Elimina los campos que no estan en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si llegan campos extra
    transform: true,  // Transforma tipos automaticamente.
  }));

  // Swagger
  const config = new DocumentBuilder()
      .setTitle('Sistema de Usuario API')
      .setDescription('API para gestion de usuarios')
      .setVersion('1.0')
      .addTag('usuario')
      .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();</code></pre>



<p class="wp-block-paragraph">Luego hay que decorar los DTOs, importando el ApiProperty y utilizar el decorador en cada campo con la estructura de datos con example y description.</p>



<pre class="wp-block-code"><code>import { IsEmail, IsNotEmpty, IsString, IsNumber, Min, Max } from 'class-validator';
import { Type } from 'class-transformer';
<strong>import { ApiProperty } from '@nestjs/swagger';</strong>

export class CreateUsuarioDto {

    <strong>@ApiProperty({ example: 'Jefferson', description: 'Nombre del usuario'})</strong>
    @IsNotEmpty({message: 'El nombre es obligatorio'})
    @IsString()
    nombre!: string;

    <strong>@ApiProperty({ example: 'jeffsp@gmail.com', description: 'Correo del usuario'})</strong>
    @IsNotEmpty()
    @IsEmail({}, {message: 'Email inválido'})
    email!: string;

    <strong>@ApiProperty({ example: '30', description: 'Edad del usuario'})</strong>
    @IsNumber()
    @Min(18, {message: 'Debe de ser mayor de 18'})
    @Max(99)
    @Type( () =&gt; Number) //Aqui se utiliza la clase transform explicitamente.
    edad!: number;
}</code></pre>



<p class="wp-block-paragraph">Finalmente, mejorar el controlador con: ApiTags, ApiOperation y ApiResponse</p>



<pre class="wp-block-code"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
import { UpdateUsuarioDto } from './dto/update-usuario.dto';
<strong>import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';</strong>

<em><strong>@ApiTags('usuario')</strong></em>
@Controller('usuario')
export class UsuarioController {
  constructor(private readonly usuarioService: UsuarioService) {}

  <strong><em>@ApiOperation({summary:'Crear nuevo usuario'})
  @ApiResponse({status:201, description:'Usuario creado'})
  @ApiResponse({status:400, description:'Datos inválidos'})</em></strong>
  @Post()
  create(@Body() createUsuarioDto: CreateUsuarioDto) {
    return this.usuarioService.create(createUsuarioDto);
  }

  @ApiOperation({summary: 'Listar todos los usuarios'})
  @ApiResponse({status:200, description:'Lista de usuarios'})
  @Get()
  findAll() {
    return this.usuarioService.findAll();
  }

  @ApiOperation({summary: 'Obtener usuario por ID'})
  @ApiResponse({status:200, description: 'Usuario encontrado'})
  @ApiResponse({status:404, description: 'Usuario no encontrado'})
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usuarioService.findOne(+id);
  }

  @ApiOperation({summary: 'Actualizar usuario'})
  @ApiResponse({status: 200, description: 'Usuario actualizado'})
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUsuarioDto: UpdateUsuarioDto) {
    return this.usuarioService.update(+id, updateUsuarioDto);
  }

  @ApiOperation({summary: 'Eliminar usuario'})
  @ApiResponse({status: 200, description: 'Usuario Eliminado'})
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usuarioService.remove(+id);
  }
}</code></pre>



<p class="wp-block-paragraph">Descripción de decoradores:</p>



<ul class="wp-block-list">
<li>@ApiTags(&#8216;usuario&#8217;) = Agrupa endpoints bajo una sección en la UI</li>



<li>@ApiProperty() = Documenta cada campo del DTO con ejemplo</li>



<li>@ApiOperation() = Describe qué hace el endpoint</li>



<li>@ApiResponse() = Documenta los posibles códigos de respuesta</li>
</ul>



<p class="wp-block-paragraph">Ello seria suficiente para tener una API documentada.</p>



<p class="wp-block-paragraph">Una vez configurado, accede a <strong><code>http://localhost:3000/api</code> </strong>para ver la documentación interactiva.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="882" height="885" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47.png" alt="" class="wp-image-297" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47.png 882w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47-300x300.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47-150x150.png 150w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47-768x771.png 768w" sizes="(max-width: 882px) 100vw, 882px" /></figure>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs uso de Validator y Transformers</title>
		<link>https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/#comments</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 23:28:07 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[REST API]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[nestjs]]></category>
		<category><![CDATA[programacion]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=283</guid>

					<description><![CDATA[Sin validaciones, un usuario puede enviar cualquier dato a tu API — un email inválido, una &#8230; <a title="Nestjs uso de Validator y Transformers" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/"><span class="screen-reader-text">Nestjs uso de Validator y Transformers</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Sin validaciones, un usuario puede enviar cualquier dato a tu API — un email inválido, una edad negativa, o campos vacíos — y llegarían directo a la base de datos. <code>class-validator</code> y <code>class-transformer</code> resuelven esto antes de que el request llegue al servicio</p>



<p class="wp-block-paragraph"><strong>Descripción</strong></p>



<p class="wp-block-paragraph"><em><strong><span style="text-decoration: underline;">class-transform </span></strong></em></p>



<p class="wp-block-paragraph">Se encarga de cambiar al tipo correcto de dato.</p>



<p class="wp-block-paragraph">En el servicio no se ven por que hacen su trabajo antes de que llegue el request ahí. El servicio recibe los datos ya validados y transformados.</p>



<p class="wp-block-paragraph">Donde actúan los transformers en el Param(&#8216;id&#8217;) del controlador.</p>



<pre class="wp-block-code has-small-font-size"><code>// Sin transform: true
@Get(':id')
findOne(@Param('id') id: string) {  // id llega como STRING "1"
  return this.usuarioService.findOne(+id); // necesitas el +id para convertir
}

// Con transform: true puedes hacer esto:
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) { // id ya llega como NUMBER
  return this.usuarioService.findOne(id); // sin el +
}</code></pre>



<p class="wp-block-paragraph">El @body tambien convierte los tipos automaticamente</p>



<pre class="wp-block-code has-small-font-size"><code>// Si el DTO tiene edad: number
// y el cliente manda: { "edad": "25" }  ← string
// transform: true lo convierte a número automáticamente antes de llegar al servicio</code></pre>



<p class="wp-block-paragraph">Se coloco un transform explicito dentro del DTO create (CreateUsuarioDto)</p>



<pre class="wp-block-code has-medium-font-size"><code>@Type(() =&gt; Number)</code></pre>



<p class="wp-block-paragraph"><strong><em><span style="text-decoration: underline;">class-validator</span></em></strong></p>



<p class="wp-block-paragraph">Valida que los datos sean los correctos.</p>



<p class="wp-block-paragraph"><strong><em><span style="text-decoration: underline;">ValidationPipe</span></em></strong></p>



<p class="wp-block-paragraph">Se encarga de orquestar ambos.</p>



<p class="wp-block-paragraph">Vamos a utilizar el validator y transformers.</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Lo primero hay que instalar las dependencias.</p>



<pre class="wp-block-code has-small-font-size"><code>npm install class-validator class-transformer</code></pre>



<p class="wp-block-paragraph">voy a modificar el archivo principal main.ts importante los ValidationPipe y agregando unas lineas para su uso.</p>



<pre class="wp-block-code has-small-font-size"><code>import { ValidationPipe } from '@nestjs/common'; // Validator

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // Elimina los campos que no estan en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si llegan campos extra
    transform: true,  // Transforma tipos automaticamente.
  }));</code></pre>



<p class="wp-block-paragraph"><strong>Sin </strong>whitelist considera que el campo extra llega al servicio<br><strong>Con </strong>whitelist: true considera que campo extra se elimina silenciosamente<br><strong>Con </strong>forbidNonWhitelisted: true >> lanza error 400</p>



<p class="wp-block-paragraph">Quedando de esta manera.</p>



<pre class="wp-block-code has-small-font-size"><code>import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common'; // Validator

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // Elimina los campos que no estan en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si llegan campos extra
    transform: true,  // Transforma tipos automaticamente.
  }));

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();</code></pre>



<p class="wp-block-paragraph">He generado una entidad llamada Usuario para poder hacer las validaciones.</p>



<pre class="wp-block-code has-small-font-size"><code>nest g resource Usuario</code></pre>



<p class="wp-block-paragraph">Entonces vamos a utilizar arrays en memoria dado que no me he conectado a una base de datos prisma.</p>



<pre class="wp-block-code has-small-font-size"><code>// Usaremos arrays en memoria, dado que no conecte a prisma
export class Usuario {
    id!: number;
    nombre!: string;
    email!: string;
    edad!: number;
}</code></pre>



<p class="wp-block-paragraph">DTOs <strong>(Simple sin validaciones)</strong></p>



<pre class="wp-block-code has-small-font-size"><code>export class CreateUsuarioDto {
    nombre!: string;
    email!: string;
    edad!: number;    
}</code></pre>



<p class="wp-block-paragraph">Ahora le voy a incluir las validaciones en una primera version.</p>



<pre class="wp-block-code has-small-font-size"><code>import { IsEmail, IsNotEmpty, IsString, IsNumber, Min, Max } from 'class-validator';
<em>import { Type } from 'class-transformer';</em>

export class CreateUsuarioDto {

    @IsNotEmpty({message: 'El nombre es obligatorio'})
    @IsString()
    nombre!: string;

    @IsNotEmpty()
    @IsEmail({}, {message: 'Email inválido'})
    email!: string;

    @IsNumber()
    @Min(18, {message: 'Debe de ser mayor de 18'})
    @Max(99)
    @Type( () =&gt; Number) //Aqui se utiliza la clase transform explicitamente.
    edad!: number;
}</code></pre>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateUsuarioDto } from './create-usuario.dto';

export class UpdateUsuarioDto extends PartialType(CreateUsuarioDto) {}</code></pre>



<p class="wp-block-paragraph">Se define el servicio.</p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
import { UpdateUsuarioDto } from './dto/update-usuario.dto';
import { Usuario } from './entities/usuario.entity'; // Importar entity

@Injectable()
export class UsuarioService {

  // Array en memoria como una base de datos temporal
  private usuarios: Usuario&#91;] = &#91;]
  private idCounter = 1;

  create(data: CreateUsuarioDto): Usuario {
    const usuario: Usuario = {
      id: this.idCounter++,
      ...data,
    };
    this.usuarios.push(usuario);
    return usuario;
  }

  findAll(): Usuario&#91;] {
    return this.usuarios;
  }

  findOne(id: number): Usuario {
    const usuario = this.usuarios.find(u =&gt; u.id === id);
    if (!usuario) throw new NotFoundException(`Usuario ${id} no encontrado`);
    return usuario;
  }

  update(id: number, data: UpdateUsuarioDto): Usuario {
    const usuario = this.findOne(id);
    Object.assign(usuario, data) // Actualiza solo los campos enviados
    return usuario;
  }

  remove(id: number): {message: string} {
    const index = this.usuarios.findIndex(u =&gt; u.id ===id);
    if (index === -1) throw new NotFoundException(`Usuario ${id} no encontrado`);
    this.usuarios.splice(index, 1);
    return {message: `Usuario ${id} eliminado`};
  }
  
}</code></pre>



<p class="wp-block-paragraph">En el controlador y el modulo, no hay mayor cambio.</p>



<p class="wp-block-paragraph">Formas de validar la operaciones CRUD de Usuario.</p>



<pre class="wp-block-code has-small-font-size"><code># Crear
Invoke-WebRequest -Uri http://localhost:3000/usuario -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Jeff","email":"jeff@gmail.com","edad":25}' | Select-Object -ExpandProperty Content

# Listar
Invoke-WebRequest -Uri http://localhost:3000/usuario -Method GET | Select-Object -ExpandProperty Content | ConvertFrom-Json

# Actualizar
Invoke-WebRequest -Uri http://localhost:3000/usuario/1 -Method PATCH -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Jeff Santillan"}' | Select-Object -ExpandProperty Content

# Eliminar
Invoke-WebRequest -Uri http://localhost:3000/usuario/1 -Method DELETE | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph"><strong>Validaciones</strong></p>



<p class="wp-block-paragraph">Error por ingreso de edad superior a 99</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="234" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-1024x234.png" alt="" class="wp-image-286" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-1024x234.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-300x69.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-768x176.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-1300x298.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44.png 1511w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Mensaje de error por que no se ingreso un nombre:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="215" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-1024x215.png" alt="" class="wp-image-287" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-1024x215.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-300x63.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-768x161.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-1300x273.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45.png 1461w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Mensaje de error por un email no valido.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="166" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-1024x166.png" alt="" class="wp-image-288" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-1024x166.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-300x49.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-768x124.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-1300x210.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46.png 1465w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Se logra la validación de manera exitosa.</p>



<pre class="wp-block-code has-small-font-size"><code>{
  "id": 1,
  "nombre": "Jeff",
  "email": "jeff@gmail.com",
  "edad": 25
}</code></pre>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs aplicación de matricula a curso – Parte IV</title>
		<link>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 20:19:15 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=272</guid>

					<description><![CDATA[Finalizando el mini proyecto, vamos a construir la estructura para el curso y su correspondiente matricula. &#8230; <a title="Nestjs aplicación de matricula a curso – Parte IV" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/"><span class="screen-reader-text">Nestjs aplicación de matricula a curso – Parte IV</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Finalizando el mini proyecto, vamos a construir la estructura para el <strong>curso </strong>y su correspondiente <strong>matricula</strong>.</p>



<p class="wp-block-paragraph">Vamos a empezar definiendo la funcionalidad para curso.</p>



<p class="wp-block-paragraph"><strong><span style="text-decoration: underline;">CURSO</span>:</strong></p>



<p class="wp-block-paragraph"><em>Entity</em>:</p>



<pre class="wp-block-code has-small-font-size"><code>export class Curso {
    id!: number;
    nombre!: string;
    codigo!: string;
    creditos!: number;
    docenteId!: number;
}</code></pre>



<p class="wp-block-paragraph"><em>DTOs, create y update:</em></p>



<pre class="wp-block-code has-small-font-size"><code>export class <strong>CreateCursoDto </strong>{
    nombre!: string;
    codigo!: string;
    creditos!: number;
    docenteId!: number;
}</code></pre>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateCursoDto } from './create-curso.dto';

export class <strong>UpdateCursoDto </strong>extends PartialType(CreateCursoDto) {}</code></pre>



<p class="wp-block-paragraph"><em>Services</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateCursoDto } from './dto/create-curso.dto';
import { UpdateCursoDto } from './dto/update-curso.dto';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class CursoService {

  constructor(private prisma: PrismaService){}

  async create(data: CreateCursoDto) {    
    // Necesario es verificar que el docente exista antes de crear un curso.
    const docente = await this.prisma.docente.findUnique({
      where: { id: data.docenteId},
    });
    if(!docente) throw new NotFoundException(`Docente ${data.docenteId} no encontrado`);
    return this.prisma.curso.create({data});
  }

  findAll() {
    return this.prisma.curso.findMany({
      include: {
        docente: true, //Muestra al docente.
      }
    });
  }

  async findOne(id: number) {
    const curso = await this.prisma.curso.findUnique({
      where: {id},
      include: { docente: true}
    });
    if(!curso) throw new NotFoundException(`Curso ${id} no encontrado`);
    return curso;
  }

  async update(id: number, data: UpdateCursoDto) {
    await this.findOne(id);
    return this.prisma.curso.update({ where: {id}, data });
  }

  async remove(id: number) {
    await this.findOne(id);
    return this.prisma.curso.delete({ where: {id} });
  }
}</code></pre>



<p class="wp-block-paragraph">Hay validaciones importantes como la existencia previa de un docente antes de crear el curso.</p>



<p class="wp-block-paragraph"><em>Controller</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { CursoService } from './curso.service';
import { CreateCursoDto } from './dto/create-curso.dto';
import { UpdateCursoDto } from './dto/update-curso.dto';

@Controller('curso')
export class CursoController {
  constructor(private readonly cursoService: CursoService) {}

  @Post()
  create(@Body() body: CreateCursoDto) {
    return this.cursoService.create(body);
  }

  @Get()
  findAll() {
    return this.cursoService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.cursoService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() body: UpdateCursoDto) {
    return this.cursoService.update(+id, body);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.cursoService.remove(+id);
  }
}</code></pre>



<p class="wp-block-paragraph"><em>Module</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Module } from '@nestjs/common';
import { CursoService } from './curso.service';
import { CursoController } from './curso.controller';

@Module({
  controllers: &#91;CursoController],
  providers: &#91;CursoService],
})
export class CursoModule {}</code></pre>



<p class="wp-block-paragraph"><strong><span style="text-decoration: underline;">MATRICULA</span>:</strong></p>



<p class="wp-block-paragraph">Para concluir</p>



<p class="wp-block-paragraph"><em>Entity</em>:</p>



<pre class="wp-block-code has-small-font-size"><code>export class Matricula {
    id!: number;
    alumnoId!: number; //FK de alumno
    cursoId!: number; //FK de curso
    fecha!: Date;
    estado!: string;
}</code></pre>



<p class="wp-block-paragraph">Aqui estamos declarando las foreign keys o las llaves que relacionan con otra tabla.</p>



<p class="wp-block-paragraph"><em>DTOs, create y update:</em></p>



<pre class="wp-block-code has-small-font-size"><code>export class <strong>CreateMatriculaDto </strong>{
    alumnoId!: number; //FK de alumno
    cursoId!: number; //FK de curso
}</code></pre>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateMatriculaDto } from './create-matricula.dto';

export class <strong>UpdateMatriculaDto </strong>extends PartialType(CreateMatriculaDto) {
    estado!: string; // ACTIVO | RETIRADO | APROBADO
}</code></pre>



<p class="wp-block-paragraph"><em>Services</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { CreateMatriculaDto } from './dto/create-matricula.dto';
import { UpdateMatriculaDto } from './dto/update-matricula.dto';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class MatriculaService {

  constructor(private prisma: PrismaService){};

  async create(data: CreateMatriculaDto) {
    // Verificar que el alumno existe
    const alumno = await this.prisma.alumno.findUnique({
      where: { id: data.alumnoId }
    });
    if(!alumno) throw new NotFoundException(`Alumno ${data.alumnoId} no encontrado`);

    // Verificar que un curso existe
    const curso = await this.prisma.curso.findUnique({
      where: { id: data.cursoId }
    });
    if(!curso) throw new NotFoundException(`Curso ${data.cursoId} no encontrado`);

    // Verificar que no esta matriculado
    const existe = await this.prisma.matricula.findUnique({
      where: {
        alumnoId_cursoId: {
          alumnoId: data.alumnoId,
          cursoId: data.cursoId
        }
      }
    });
    if (existe) throw new BadRequestException('El alumno ya está matriculado en este curso');

    return this.prisma.matricula.create({
      data, 
      include: {
        alumno: true,
        curso: { include: {docente: true} }
      }
    });

  }

  findAll() {
    return this.prisma.matricula.findMany({
      include: {
        alumno: true,
        curso: { include: { docente: true }} // Curso con su correspondiente docente.
      }
    })
  }

  async findOne(id: number) {
    const matricula = await this.prisma.matricula.findUnique({
      where: {id},
      include: {
        alumno: true,
        curso: { include: { docente: true}}
      }
    });
    if (!matricula) throw new NotFoundException(`Matricula ${id} no encontrada`);
    return matricula;
  }

  async update(id: number, data: UpdateMatriculaDto) {
    await this.findOne(id);
    return this.prisma.matricula.update({ where: {id}, data})
  }

  async remove(id: number) {
    await this.findOne(id) ;
    return this.prisma.matricula.delete({ where : {id} });
  }

  // Curso de un alumno especifico
  findByAlumno(alumnoId: number){
    return this.prisma.matricula.findMany({
      where: { alumnoId },
      include: {
        curso: { include : {docente: true}}
      }
    });
  }

}</code></pre>



<p class="wp-block-paragraph"><em>Controller</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { MatriculaService } from './matricula.service';
import { CreateMatriculaDto } from './dto/create-matricula.dto';
import { UpdateMatriculaDto } from './dto/update-matricula.dto';

@Controller('matricula')
export class MatriculaController {
  constructor(private readonly matriculaService: MatriculaService) {}

  @Post()
  create(@Body() body: CreateMatriculaDto) {
    return this.matriculaService.create(body);
  }

  @Get()
  findAll() {
    return this.matriculaService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.matriculaService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() body: UpdateMatriculaDto) {
    return this.matriculaService.update(+id, body);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.matriculaService.remove(+id);
  }

  // Curso de un alumno especifico
  @Get('alumno/:alumnoId')
  findByAlumno(@Param('alumnoId') alumnoId: string){
    return this.matriculaService.findByAlumno(+alumnoId);
  }

}</code></pre>



<p class="wp-block-paragraph"><em>Module</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Module } from '@nestjs/common';
import { MatriculaService } from './matricula.service';
import { MatriculaController } from './matricula.controller';

@Module({
  controllers: &#91;MatriculaController],
  providers: &#91;MatriculaService],
})
export class MatriculaModule {}</code></pre>



<p class="wp-block-paragraph">Con esto concluimos el registro de matricula de un alumno dentro de un curso.</p>



<p class="wp-block-paragraph"><strong>Operaciones para Curso</strong></p>



<p class="wp-block-paragraph">Crear un curso con el docente con ID 1</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Matematicas","codigo":"MAT101","creditos":4,"docenteId":1}' | Select-Object -ExpandProperty Content</code></pre>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Programacion","codigo":"PRG101","creditos":5,"docenteId":1}' | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Listar cursos</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso -Method GET -<strong>UseBasicParsing </strong>| Select-Object -ExpandProperty Content | <strong>ConvertFrom-Json</strong></code></pre>



<p class="wp-block-paragraph">Obtener curso por ID</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso/1 -Method GET | Select-Object -ExpandProperty Content | ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">Actualizar curso</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso/1 -Method PATCH -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Matematica Avanzada"}' | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Eliminar</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso/1 -Method DELETE | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Todas las operaciones ya estan disponibles para el curso.</p>



<p class="wp-block-paragraph"><strong>Operaciones para Matricula</strong></p>



<p class="wp-block-paragraph">Matricular alumno a un curso.</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/matricula -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"alumnoId":1,"cursoId":1}' -UseBasicParsing | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Ver todas las matriculas</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/matricula -Method GET -UseBasicParsing | Select-Object -ExpandProperty Content | ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">Curso de un alumno en especifico.</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/matricula/alumno/1 -Method GET -UseBasicParsing | Select-Object -ExpandProperty Content | ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">¡Saludos!</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Multiples version de Python en Linux con Ubuntu</title>
		<link>https://jeffsantillan.com/blog/multiples-version-de-python-en-linux-con-ubuntu/</link>
					<comments>https://jeffsantillan.com/blog/multiples-version-de-python-en-linux-con-ubuntu/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 03 Apr 2026 07:49:52 +0000</pubDate>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=49</guid>

					<description><![CDATA[Ubuntu viene con Python preinstalado, pero con dos particularidades: se llama python3 en lugar de python, &#8230; <a title="Multiples version de Python en Linux con Ubuntu" class="hm-read-more" href="https://jeffsantillan.com/blog/multiples-version-de-python-en-linux-con-ubuntu/"><span class="screen-reader-text">Multiples version de Python en Linux con Ubuntu</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ubuntu viene con Python preinstalado, pero con dos particularidades: se llama <code>python3</code> en lugar de <code>python</code>, y su versión está atada al sistema operativo. Actualizarla directamente es arriesgado, ya que varios componentes del SO dependen de ella y podría romperse la instalación.</p>



<p class="wp-block-paragraph">Lo que haremos es instalar una versión más reciente de forma paralela, pudiendo así alternar entre versiones: una estable que usa el sistema por defecto y otra más moderna para pruebas.</p>



<p class="wp-block-paragraph">Versión actual</p>



<pre class="wp-block-code"><code>python3 --version</code></pre>



<p class="wp-block-paragraph">Asi que vamos a instalar una versión nueva en paralelo, haciendo uso del PPA de deadsnakes.</p>



<pre class="wp-block-code"><code># Agregar el repositorio

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update

# Instalar la versión que quieras (ejemplo: 3.12 o 3.13)

sudo apt-get install python3.14</code></pre>



<p class="wp-block-paragraph">Verificar la versión</p>



<pre class="wp-block-code"><code>python3.14 --version</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="601" height="113" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-1.png" alt="" class="wp-image-50" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-1.png 601w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-1-300x56.png 300w" sizes="auto, (max-width: 601px) 100vw, 601px" /></figure>



<p class="wp-block-paragraph">Vamos a realizar que python3 apunte a la version mas reciente.</p>



<pre class="wp-block-code"><code># Ver las versiones instaladas.

ls /usr/bin/python3*</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="728" height="94" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-2.png" alt="" class="wp-image-51" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-2.png 728w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-2-300x39.png 300w" sizes="auto, (max-width: 728px) 100vw, 728px" /></figure>



<pre class="wp-block-code"><code># Configurar alternativas

sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.14 1</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="169" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-4-1024x169.png" alt="" class="wp-image-53" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-4-1024x169.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-4-300x49.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-4-768x127.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-4.png 1347w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<pre class="wp-block-code"><code># Elegir cual utilizar

sudo update-alternatives --config python3 </code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="253" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-5-1024x253.png" alt="" class="wp-image-54" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-5-1024x253.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-5-300x74.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-5-768x189.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-5.png 1042w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Finalmente, hacer que python funcione sin el 3 (python3)</p>



<pre class="wp-block-code"><code>sudo apt-get install python-is-python3</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="473" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-6-1024x473.png" alt="" class="wp-image-55" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-6-1024x473.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-6-300x139.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-6-768x355.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-6.png 1221w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Considera, que Ubuntu internamente rutas absolutas como /usr/bin/python3.13 para sus herramientas criticas, no el symlink genérico.</p>



<p class="wp-block-paragraph">update-alternatives solo cambia el symlink, no afecta, elimina o reemplaza el python 3.13</p>



<p class="wp-block-paragraph"><strong><em>¿Problemas con el pip?</em></strong></p>



<p class="wp-block-paragraph">Es posible que python-apt sigue apuntando a 3.13 sera mejor reinstalar.</p>



<pre class="wp-block-code"><code>sudo apt-get install --reinstall python3-apt</code></pre>



<p class="wp-block-paragraph">Además, instalar pip para Python 3.14</p>



<pre class="wp-block-code"><code>sudo apt-get install python3-pip</code></pre>



<p class="wp-block-paragraph">Validar y verificar las versiones</p>



<pre class="wp-block-code"><code>python3 --version
pip3 --version
pip3 list</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="520" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-7-1024x520.png" alt="" class="wp-image-59" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-7-1024x520.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-7-300x152.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-7-768x390.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-7.png 1281w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Saludos</p>



<p class="wp-block-paragraph">Python: <a href="https://www.python.org/">Enlace</a></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/multiples-version-de-python-en-linux-con-ubuntu/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Extensión de Claude en Visual Studio Code</title>
		<link>https://jeffsantillan.com/blog/extension-de-claude-en-visual-studio-code/</link>
					<comments>https://jeffsantillan.com/blog/extension-de-claude-en-visual-studio-code/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 03 Apr 2026 07:04:23 +0000</pubDate>
				<category><![CDATA[Claude]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Visual Studio Code]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=40</guid>

					<description><![CDATA[Si quieres ejecutar Claude como extension de Visual Studio Code (VSCode), es necesario tener instalado previamente &#8230; <a title="Extensión de Claude en Visual Studio Code" class="hm-read-more" href="https://jeffsantillan.com/blog/extension-de-claude-en-visual-studio-code/"><span class="screen-reader-text">Extensión de Claude en Visual Studio Code</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Si quieres ejecutar Claude como extension de Visual Studio Code (VSCode), es necesario tener instalado previamente el mismo y para ello vamos instalarlo en Linux Ubuntu 25.10</p>



<p class="wp-block-paragraph"><strong>Modo con SNAP.</strong></p>



<pre class="wp-block-code"><code>sudo snap install code --classic</code></pre>



<p class="wp-block-paragraph">Para desinstalar </p>



<pre class="wp-block-code"><code>sudo snap remove code</code></pre>



<p class="wp-block-paragraph">Es requerido el flag de classic, dado que la instalacion necesita de acceso al sistema de archivos.</p>



<p class="wp-block-paragraph"><strong>Alternativa con firma de Microsoft.</strong></p>



<p class="wp-block-paragraph">Sera necesario ejecutar el proximo script.</p>



<pre class="wp-block-code"><code># Agregar la clave GPG de Microsoft

curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /usr/share/keyrings/microsoft.gpg &gt; /dev/null

# Agregar el repo de VSCode

echo "deb &#91;arch=amd64 signed-by=/usr/share/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | sudo tee /etc/apt/sources.list.d/vscode.list

# Actualizar e Instalar

sudo apt-get update
sudo apt-get install code</code></pre>



<p class="wp-block-paragraph">Si deseas ejecutar el programa VSCode desde la terminal solo digita</p>



<pre class="wp-block-code"><code>code .</code></pre>



<p class="wp-block-paragraph">Luego ir a la seccion de extensiones con CTRL + SHIFT + X</p>



<p class="wp-block-paragraph">y buscar Claude de Anthropic</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="237" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-02-00-14-1024x237.png" alt="" class="wp-image-41" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-02-00-14-1024x237.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-02-00-14-300x69.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-02-00-14-768x178.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-02-00-14.png 1124w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Dar clic en Install con el Auto Update marcado.</p>



<p class="wp-block-paragraph">Finalmente. Tendra un icono con el Claude, listo para iniciar sesión.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="362" height="293" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image.png" alt="" class="wp-image-42" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image.png 362w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/image-300x243.png 300w" sizes="auto, (max-width: 362px) 100vw, 362px" /></figure>



<p class="wp-block-paragraph">Es necesario autentica con Claude.ai</p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/extension-de-claude-en-visual-studio-code/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Instalar Claude Code en Linux Ubuntu 25.10</title>
		<link>https://jeffsantillan.com/blog/instalar-claude-code-en-linux-ubuntu-25-10/</link>
					<comments>https://jeffsantillan.com/blog/instalar-claude-code-en-linux-ubuntu-25-10/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 03 Apr 2026 06:33:59 +0000</pubDate>
				<category><![CDATA[Claude]]></category>
		<category><![CDATA[IA]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Visual Studio Code]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=35</guid>

					<description><![CDATA[Hola Para realizar la instalación de Claude Code en Linux Ubuntu es necesario que previamente tengas &#8230; <a title="Instalar Claude Code en Linux Ubuntu 25.10" class="hm-read-more" href="https://jeffsantillan.com/blog/instalar-claude-code-en-linux-ubuntu-25-10/"><span class="screen-reader-text">Instalar Claude Code en Linux Ubuntu 25.10</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Hola</p>



<p class="wp-block-paragraph">Para realizar la instalación de Claude Code en Linux Ubuntu es necesario que previamente tengas instalado el curl.</p>



<p class="wp-block-paragraph">Instalar Curl</p>



<pre class="wp-block-code"><code>sudo apt-get install curl</code></pre>



<p class="wp-block-paragraph">Instalación de Claude Code</p>



<pre class="wp-block-code"><code>curl -fsSL https://claude.ai/install.sh | bash</code></pre>



<p class="wp-block-paragraph">Si logra la instalación.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="944" height="518" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-11.png" alt="" class="wp-image-36" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-11.png 944w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-11-300x165.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-11-768x421.png 768w" sizes="auto, (max-width: 944px) 100vw, 944px" /></figure>



<p class="wp-block-paragraph">como indica el resultado, el programa Claude se ubica en</p>



<pre class="wp-block-code"><code>~/local/bin/claude</code></pre>



<p class="wp-block-paragraph">Solicitar conocer la version del programa</p>



<pre class="wp-block-code"><code><code>~/local/bin/claude --version</code></code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="710" height="85" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-50.png" alt="" class="wp-image-37" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-50.png 710w, https://jeffsantillan.com/blog/wp-content/uploads/2026/04/Captura-desde-2026-04-03-00-12-50-300x36.png 300w" sizes="auto, (max-width: 710px) 100vw, 710px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/instalar-claude-code-en-linux-ubuntu-25-10/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Certificado de Claude para Arquitectos</title>
		<link>https://jeffsantillan.com/blog/certificado-de-claude-para-arquitectos/</link>
					<comments>https://jeffsantillan.com/blog/certificado-de-claude-para-arquitectos/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Mon, 16 Mar 2026 04:08:10 +0000</pubDate>
				<category><![CDATA[Arquitecto]]></category>
		<category><![CDATA[Claude]]></category>
		<category><![CDATA[IA]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[architect]]></category>
		<category><![CDATA[certified]]></category>
		<category><![CDATA[claude]]></category>
		<category><![CDATA[inteligencia artificial]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=31</guid>

					<description><![CDATA[Anthropic ha lanzado un nuevo certificado llamado «Claude Certified Architect» de forma gratuita para 5000 empleados &#8230; <a title="Certificado de Claude para Arquitectos" class="hm-read-more" href="https://jeffsantillan.com/blog/certificado-de-claude-para-arquitectos/"><span class="screen-reader-text">Certificado de Claude para Arquitectos</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Anthropic ha lanzado un nuevo certificado llamado «<strong>Claude Certified Architect</strong>» de forma gratuita para 5000 empleados que pertenezcan a la lista de empresas asociadas a Anthropic en un lanzamiento anticipado.</p>



<p class="wp-block-paragraph">Luego el certificado <strong>tendrá un costo de $99 y será de 60 preguntas,</strong> supervisado y tiene una duración de 120 minutos como máximo, con un solo intento, es un examen de nivel 301.</p>



<p class="wp-block-paragraph">Tienes que demostrar la experiencia en la creación de aplicaciones de nivel profesional en Claude.</p>



<p class="wp-block-paragraph">De momento, solo nos proporciona una guia de estudio para aquellas personas que no aplican al certificado por no tener un correo de socios de Anthropic de forma anticipada. Ve dandote una idea con la <a href="https://everpath-course-content.s3-accelerate.amazonaws.com/instructor%2F8lsy243ftffjjy1cx9lm3o2bw%2Fpublic%2F1773274827%2FClaude+Certified+Architect+%E2%80%93+Foundations+Certification+Exam+Guide.pdf" target="_blank" rel="noreferrer noopener">Guía de Estudio</a></p>



<p class="wp-block-paragraph">Incluso si aún no formas parte de sus socios, tampoco podrias acceder al examen de simulación por ahora. <a href="https://anthropic.skilljar.com/anthropic-certification-practice-exam" target="_blank" rel="noreferrer noopener">Examen de simulación</a>, te recomiendan lograr 900 de 1000 para recien poder rendir el examen oficial.</p>



<p class="wp-block-paragraph"><strong>Dominios de evaluación</strong></p>



<ul class="wp-block-list">
<li>Arquitectura y orquestación de agentes (27%)</li>



<li>Diseño de herramientas e integración de MCP (18%)</li>



<li>Configuración de código y flujos de trabajo en Claude (20%)</li>



<li>Ingenieria rápida y resultados estructurados (20%)</li>



<li>Gestión del Contexto y la fiabilidad (15%)</li>
</ul>



<p class="wp-block-paragraph">Sugieren que lleves previamente los cursos de:</p>



<ul class="wp-block-list">
<li>Claude 101</li>



<li>Claude Code en acción</li>



<li>Introducción al Protocolo de Contexto</li>



<li>Compilación con la API de Claude.</li>
</ul>



<p class="wp-block-paragraph">Revisa los cursos disponibles en la <a href="https://jeffsantillan.com/blog/academia-antropica/">academia antrópica</a>.</p>



<p class="wp-block-paragraph">Descubre más en su web por su <a href="https://anthropic.skilljar.com/claude-certified-architect-foundations-access-request" target="_blank" rel="noreferrer noopener">enlace</a> principal.</p>



<p class="wp-block-paragraph"><strong>Start Building with Claude</strong> &#8211; <a href="https://platform.claude.com/docs/en/home">Here</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/certificado-de-claude-para-arquitectos/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Academia Antrópica</title>
		<link>https://jeffsantillan.com/blog/academia-antropica/</link>
					<comments>https://jeffsantillan.com/blog/academia-antropica/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Mon, 16 Mar 2026 03:44:46 +0000</pubDate>
				<category><![CDATA[Claude]]></category>
		<category><![CDATA[IA]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[claude]]></category>
		<category><![CDATA[inteligencia artificial]]></category>
		<category><![CDATA[plataforma aprendizaje]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=24</guid>

					<description><![CDATA[13 cursos para aquellas personas que le interesa aprender más de Claude una potente IA. Anthropic &#8230; <a title="Academia Antrópica" class="hm-read-more" href="https://jeffsantillan.com/blog/academia-antropica/"><span class="screen-reader-text">Academia Antrópica</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">13 cursos para aquellas personas que le interesa aprender más de Claude una potente IA.</p>



<p class="wp-block-paragraph"><strong>Anthropic Academy</strong></p>



<p class="wp-block-paragraph">La empresa ha lanzado un conjunto de cursos, desde guías de desarrollo de API hasta las mejores practicas de implementación empresarial, la academia Antrópica está ofreciendo todo lo que necesitas.</p>



<p class="wp-block-paragraph"><a href="https://www.anthropic.com/learn">https://www.anthropic.com/learn</a></p>



<p class="wp-block-paragraph"><strong>01 &#8211; Claude 101</strong></p>



<p class="wp-block-paragraph">Aprender a usar Claude para tareas laborales cotidianas, comprende sus funciones principales y explora recursos para un aprendizaje más avanzado sobre otros temas.</p>



<p class="wp-block-paragraph">Duración: 14 lecciones</p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/claude-101">https://anthropic.skilljar.com/claude-101</a></p>



<p class="wp-block-paragraph"><strong>02 &#8211; Claude Code en Acción</strong></p>



<p class="wp-block-paragraph">Integra Claude Code en tu flujo de trabajo de desarrollo</p>



<p class="wp-block-paragraph">Duración: 21 lecciones.</p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/claude-code-in-action">https://anthropic.skilljar.com/claude-code-in-action</a></p>



<p class="wp-block-paragraph"><strong>03 &#8211; Introducción a las habilidades de agente</strong></p>



<p class="wp-block-paragraph">Aprende a crear, configurar y compartir habilidades (Skills) en Claude Code:</p>



<p class="wp-block-paragraph">Instrucciones Markdown reutilizables que Claude aplica automáticamente a las tareas adecuadas en el momento preciso. El curso te guía desde la creación de la primera Skill hasta la distribución entre equipos y la resolución de problemas comunes.</p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/introduction-to-agent-skills">https://anthropic.skilljar.com/introduction-to-agent-skills</a></p>



<p class="wp-block-paragraph"><strong>04 &#8211; Competencia con IA: Marco y Fundamentos</strong></p>



<p class="wp-block-paragraph">Aprende a colaborar con sistemas de IA de forma eficaz, eficiente, ética y segura.</p>



<p class="wp-block-paragraph"><em>Duración: 1.1 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/ai-fluency-framework-foundations">https://anthropic.skilljar.com/ai-fluency-framework-foundations</a></p>



<p class="wp-block-paragraph"><strong>05 &#8211; Construyendo con API de Claude</strong></p>



<p class="wp-block-paragraph">Este curso integral abarca todo el espectro del trabajo con modelos antrópicos utilizando la API de Claude.</p>



<p class="wp-block-paragraph"><em>Duración: 8.1 Hrs</em></p>



<p class="wp-block-paragraph"><a href="http://anthropic.skilljar.com/claude-with-the-anthropic-api">http://anthropic.skilljar.com/claude-with-the-anthropic-api</a></p>



<p class="wp-block-paragraph"><strong>06 &#8211; Introducción al Protocolo de Contexto de Modelo (MCP)</strong></p>



<p class="wp-block-paragraph">Aprende a crear servidores y clientes del Protocolo de Contexto de Modelo (MCP) desde cero usando Python. Domina las tres primitivas principales de MCP (Herramientas, recursos y mensajes) para conectar Claude con servicios externos.</p>



<p class="wp-block-paragraph"><em>Duración: 1 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/introduction-to-model-context-protocol">https://anthropic.skilljar.com/introduction-to-model-context-protocol</a></p>



<p class="wp-block-paragraph"><strong>07 &#8211; Protocolo de Contexto del Modelo: Temas avanzados</strong></p>



<p class="wp-block-paragraph">Descubra patrones avanzados de implementación del MCP que incluye muestreo, notificaciones, acceso al sistema de archivos y mecanismos de transporte para el desarrollo de servidores MCP en producción.</p>



<p class="wp-block-paragraph"><em>Duración: 1.1 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/model-context-protocol-advanced-topics">https://anthropic.skilljar.com/model-context-protocol-advanced-topics</a></p>



<p class="wp-block-paragraph"><strong>08 &#8211; Claude con Google Cloud’s Vertex AI</strong></p>



<p class="wp-block-paragraph">El curso abarca el uso de modelos Claude a través de la plataforma Vertex AI de Google Cloud. En donde se implementará funciones clave de la API, como la transmisión de datos y el uso de herramientas, en donde se creará flujos de trabajo sistemáticos para la evaluación de indicadores, comprenderás los sistemas RAG y se investigara las arquitecturas de los agentes.</p>



<p class="wp-block-paragraph"><em>Duración: 8 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/claude-with-google-vertex">https://anthropic.skilljar.com/claude-with-google-vertex</a></p>



<p class="wp-block-paragraph"><strong>09 &#8211; Claude con Amazon Bedrock</strong></p>



<p class="wp-block-paragraph">Abarca el uso de modelos Claude a través de la API de AWS Bedrock, desde solicitudes básicas hasta implementaciones avanzadas de agentes. Aprendes a realizar la llamada a la API, implementar el uso de herramientas, crear canalizaciones RAG, trabajar con servidores MCP y aprovechar funciones como Claude Code y el uso de computadoras para la automatización.</p>



<p class="wp-block-paragraph"><em>Duración: 8 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/claude-in-amazon-bedrock">https://anthropic.skilljar.com/claude-in-amazon-bedrock</a></p>



<p class="wp-block-paragraph"><strong>10 &#8211; Alfabetización en IA para educadores.</strong></p>



<p class="wp-block-paragraph">El curso capacita a profesores, diseñadores instruccionales y lideres educativos para aplicar fluidez en la IA a su propia práctica docente y estrategia institucional.</p>



<p class="wp-block-paragraph"><em>Duración: 0.4 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/ai-fluency-for-educators">https://anthropic.skilljar.com/ai-fluency-for-educators</a></p>



<p class="wp-block-paragraph"><strong>11 &#8211; Enseñar fluides en IA</strong></p>



<p class="wp-block-paragraph">Curso de capacitación al profesorado, a los diseñadores de instrucciones y a otros profesionales para enseñar a evaluar la competencia de la IA en entornos dirigidos por un instructor.</p>



<p class="wp-block-paragraph"><em>Duración: 0.6 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/teaching-ai-fluency">https://anthropic.skilljar.com/teaching-ai-fluency</a></p>



<p class="wp-block-paragraph"><strong>12 &#8211; Alfabetización en IA para estudiantes</strong></p>



<p class="wp-block-paragraph">El curso capacita a estudiantes para desarrollar habilidades de dominio en IA que mejoran el aprendizaje, la planificación profesional y el éxito académico a través de una colaboración responsable de la IA.</p>



<p class="wp-block-paragraph"><em>Duración: 0.5 Hrs</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/ai-fluency-for-students">https://anthropic.skilljar.com/ai-fluency-for-students</a></p>



<p class="wp-block-paragraph"><strong>13 &#8211; IA para organizaciones sin fines de lucro</strong></p>



<p class="wp-block-paragraph">Curso desarrollado para organizaciones sin fines de lucro para el desarrollo de habilidades en IA con el fin de aumentar el impacto y eficiencia de sus organizaciones, sin dejar de ser fieles a su misión y valores.</p>



<p class="wp-block-paragraph"><em>Duración: 0.9 Hr</em></p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com/ai-fluency-for-nonprofits">https://anthropic.skilljar.com/ai-fluency-for-nonprofits</a></p>



<p class="wp-block-paragraph">Puedes acceder a todos los cursos de Anthropic desde el siguiente enlace:</p>



<p class="wp-block-paragraph"><a href="https://anthropic.skilljar.com">https://anthropic.skilljar.com</a></p>



<p class="wp-block-paragraph">Mas información en el blog del proyecto. <a href="https://claude.com/blog">Enlace</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/academia-antropica/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
