logo cosasdedevs
Como enviar parámetros en la url con FastAPI

Como enviar parámetros en la url con FastAPI



My Profile
Feb 09, 2022

¡Muy buenas! En este tutorial aprenderás a enviar parámetros en la URL con FastAPI de tal forma que podrás acceder a ellos más adelante y dar dinamismo a tu API.

Cosas que vamos a ver en este tutorial

He intentado hacer este tutorial lo más completo posible, en él vais a ver:

¡Y dicho esto comenzamos!

Cuáles son los parámetros que se envían por url (path parameters)

Este tipo de parámetros a diferencia de los query params se envían dentro de la URL y los utilizamos para dar dinamismo a un endpoint. 

Un ejemplo de path parameters podrían ser el último segmento de las urls de los posts en mi web:

https://cosasdedevs.com/posts/notificaciones-errores-httpexception-fastapi/
https://cosasdedevs.com/posts/fastapi-form-recibir-parametros-formulario/

Como podéis observar ambas urls comparten esta parte:

https://cosasdedevs.com/posts/

Y luego tienen una parte dinámica que sería un texto descriptivo (notificaciones-errores-httpexception-fastapi y fastapi-form-recibir-parametros-formulario) que funciona como una clave en mi base de datos. Cuando accedemos a esta url, capturo esa clave y obtengo el post al que corresponde.

Esto evita que por ejemplo tuviera que crear una ruta única para cada post.

Como enviar parámetros en la url

Para enviar parámetros por la URL con FastAPI solo debemos añadirlos dentro de la ruta entre llaves {} y volver a añadirlos con el mismo nombre en la función.

from fastapi import FastAPI

app = FastAPI()

@app.get("/posts/{slug_title}")
async def get_post(slug_title):
    return [slug_title]

@app.get("/posts/v2/{year}/{month}/{slug_title}")
async def get_post_v2(year, month, slug_title):
    return [year, month, slug_title]

Como podéis observar, podemos enviar todos los parámetros que queramos. FastAPI se encargará de asignarlos a las variables en la función.

Por qué el tipado es tu aliado

Como ya sabemos de sobra FastAPI nos recomienda (y a veces obliga) a usar el tipado. Esto lejos de ser un problema nos facilita el trabajo, ya que nos convertirá el tipo de dato que enviemos al requerido, por ejemplo vamos a crear un endpoint que recibirá un número y que lo guardará en un parámetro llamado id:

from fastapi import FastAPI

app = FastAPI()

valid_ids = [1, 2, 3, 4]

@app.get("/posts/{id}")
async def get_post(id):
    if id in valid_ids:
        return ['Lo tenemos']
    else:
        return ['No lo encuentro']

Tal y como está ahora aunque enviemos un número no lo convertirá a entero, por lo tanto, aunque enviásemos 1, 2, 3 o 4, nunca lo encontraría, pero si añadimos el tipado en la función esto se soluciona, ya que FastAPI se encarga de convertirlo a entero y además si enviamos algo que no sea un número lanzará un error, dos por uno 💪:

from fastapi import FastAPI

app = FastAPI()

valid_ids = [1, 2, 3, 4]

@app.get("/posts/{id}")
async def get_post(id: int):
    if id in valid_ids:
        return ['Lo tenemos']
    else:
        return ['No lo encuentro']

Además, si queremos ir un paso más allá y añadir más validaciones y metadatos, podemos utilizar la función Path de FastAPI.

Y bueno ¿Qué validaciones y metadatos podemos emplear? Pues bien para validaciones pues por ejemplo podemos decir que si es un string que mínimo tenga 3 caracteres y máximo 10. Para enteros también podemos añadir rangos o usar expresiones regulares para que el parámetro tenga un formato en concreto. En cuanto a metadatos podemos añadir un título, descripción y ejemplos que luego podremos ver en la documentación que genera FastAPI automáticamente.

En el siguiente ejemplo hemos añadido un límite de caracteres entre 5 y 15 para el parámetro slug_title. Si nos llega algo fuera de ese rango la API mostrará un error.

from fastapi import FastAPI
from fastapi import Path

app = FastAPI()

@app.get("/posts/{slug_title}")
async def get_post(slug_title: str = Path(
    ...,
    min_length=5,
    max_length=15,
    description="Aquí va la descripción de lo que hace el campo",
    example="este-es-un-post"
)):
    return [slug_title]

Como podéis ver, importamos Path y después de declarar el parámetro slug_title lo igualamos a la función Path .Después como primer parámetro recibe los tres puntos (...) que indican que el campo será obligatorio (la verdad es que para este tipo de datos no me imagino cuál encaje tendría un campo de este tipo como opcional) y posteriormente ya enviamos los parámetros que necesitemos.

En nuestro caso como es de tipo string podemos usar los parámetros min_length y max_length si queremos definir un tamaño mínimo y máximo para la variable.

Si enviamos una petición dentro del rango todo funcionará correctamente:

Pero si nos quedamos cortos nos mostrará un error:

Además, como añadimos también una descripción y ejemplo, si ahora vamos a la documentación podremos ver tanto la descripción como el ejemplo que hemos añadido. También indica el tamaño mínimo y máximo que puede contener así como su tipo:

Si queréis ver más información acerca de la función Path y como validar distintos tipos de datos os dejo el enlace a la doc 👇

https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/

Predefinir los valores que se pueden enviar por la url

Gracias a la clase Enum de Python podemos definir dentro de una clase los valores que queremos permitir, de esta forma FastAPI validará automáticamente si estamos enviando un valor válido o no y si no es así lanzará un error:

from fastapi import FastAPI
from enum import Enum

class ModelCategory(str, Enum):
    volvo = "volvo"
    seat = "seat"
    citroen = "citroen"

app = FastAPI()

@app.get("/cars/{category}")
async def get_category(category: ModelCategory):
    return [category]

En este ejemplo hemos creado la clase ModelCategory la cual extiende de Enum y dentro añadimos las variables con sus valores permitidos. En la función solo debemos indicar que la variable será de tipo ModelCategory y de esta manera el endpoint solo permitirá recibir los parámetros volvo, seat y citroen.

Si no enviamos uno de estos tres valores la respuesta de la API es la siguiente:

Como podéis observar lanza un error con bastante información indicando el fallo y los valores permitidos.

Si enviamos un parámetro válido funcionará perfectamente.

Parámetros de ruta que apuntan a archivos estáticos

También podemos utilizar los parámetros Path para acceder a archivos estáticos dentro de nuestro proyecto. Para este ejemplo he generado una carpeta llamada files y dentro he añadido un archivo llamado texto.txt donde he añadido un mensaje.

Si quisiéramos poder acceder a este archivo o a cualquier otro que estuviera en esta carpeta mediante su nombre podríamos hacerlo de la siguiente manera:

import os
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi.responses import FileResponse

app = FastAPI()

@app.get("/files/{file}")
async def get_file(file: str):
    if not os.path.isfile(f'./files/{file}'):
        raise HTTPException(status_code=404, detail='File not found')

    return FileResponse(f'./files/{file}')

Para que este ejemplo funcione correctamente como parámetro de Path deberíamos recibir el mismo nombre de archivo existente en nuestra carpeta, si no es así lanzamos el error con HttpException. Si existe continuamos con el proceso y utilizamos FileResponse de FastAPI el cual recibirá por parámetro la ruta del archivo, se encarga de leerlo y mostrarlo al usuario indicando las cabeceras pertinentes según el tipo de archivo que queramos mostrar.

En mi caso la respuesta es esta:

Una cosa importante es que aunque la ruta que he definido en el endpoint y la carpeta tienen el mismo nombre no tienen por qué coincidir, ya que somos nosotros los que estamos manejando los nombres en la función y en la ruta.

Para evitar confusiones con esta parte del tutorial he subido el proyecto a un repositorio por si necesitáis echarle un vistazo 👇

https://github.com/albertorc87/read-files-fastapi

Y bueno, esto es todo por este tutorial, si tenéis alguna duda recordad que tenéis la caja de comentarios para preguntar lo que sea que yo os intentaré ayudar 💪.

Espero que este post te ayude y como siempre, te recomiendo seguirme en Twitter para estar al tanto de los nuevo contenido. Ahora también puedes seguirme en Instagram donde estoy subiendo tips, tutoriales en vídeo e información sobre herramientas para developers.

Por último os dejo mi guía para aprender a trabajar con APIs donde explico todo el funcionamiento de una API, el protocolo HTTP y veremos como construir una API con arquitectura REST.

Nos leemos 👋.

4193 vistas

🐍 Sígueme en Twitter

Si te gusta el contenido que subo y no quieres perderte nada, sígueme en Twitter y te avisaré cada vez que cree contenido nuevo 💪
Luego ¡Te sigo!

Nos tomamos en serio tu privacidad

Utilizamos cookies propias y de terceros para recopilar y analizar datos sobre la interacción de los usuarios con cosasdedevs.com. Ver política de cookies.