DEV Community

IagoLast
IagoLast

Posted on

3 consejos para diseñar APIs

OJOCUIDAO: Lo escrito a continuaicón es simplemente mi opinión en base a mi experiencia. Si te parece que estoy diciendo una burrada te agradezco que me lo comentes

Utiliza enums en lugar de valores booleanos.

Imaginemos que trabajamos en una empresa de tarjetas. Imaginemos que estamos diseñando un endpoint GET /cards/id que te devuelve los datos de una tarjeta de crédito.

A grandes rasgos estos datos son el nombre del propietario, el número de tarjeta, la fecha de caducidad, el código de seguridad, el saldo y el estado de la tarjeta. (Activada o desactivada)

Con esta definición, mucha gente tenderá a representar este concepto de la siguiente forma (El número de tarjeta se llama PAN):

{
      name: string,
      pan: number; 
      exp_year: number;
      exp_month: number;
      is_active: boolean; 
}
Enter fullscreen mode Exit fullscreen mode

en principio, parece que tiene sentido pero imaginemos que añadimos una nueva característica a nuestro sistema y ahora permitimos que una tarjeta pueda estar "inactiva, activa y también congelada"

🔥🔥🔥🔥🔥

El diseño anterior utiliza un valor booleano para el estado is_active por lo que para representar si está congelada tendríamos que añadir otro valor booleano is_frozen

{
      name: string,
      pan: number;
      exp_year: number;
      exp_month: number;
      is_active: boolean;
      is_frozen: boolean;
}
Enter fullscreen mode Exit fullscreen mode

Esto nos deja con 4 posibles combinaciones:

is_active is_frozen
no no
no yes
yes no
yes yes

lo malo es que desde el punto de vista del producto algunas de estas combinaciones pueden no tener sentido. Por ejemplo si esta congelada, da igual si esta activada o no.

y que pasa si queremos representar que está bloqueada por fraude?

{
      name: string,
      pan: number;
      exp_year: number;
      exp_month: number;
      is_active: boolean;
      is_frozen: boolean;
      is_blocked: boolean;
}
Enter fullscreen mode Exit fullscreen mode

Poco a poco nuestro diseño se va complicando y empezamos a tener más y más combinaciones de valores booleanos.

Si alguien quiere hacer una aplicación y dibujar la tarjeta representando el estado correcto tendrá que hacer una serie de comprobaciones en esos campos para conseguir la representación adecuada.

Ahora veamos qué pasaría si utilizamos un enum (un string con una serie de valores posibles) para representar este estado:

{
    name: string,
    pan: number; // El número de tarjeta se llama PAN
    exp_year: number;
    exp_month: number;
    status: 'active' | 'inactive';
}
Enter fullscreen mode Exit fullscreen mode

Después de añadir el requisito de poder congelar la tarjeta:

{
    name: string,
    pan: number; // El número de tarjeta se llama PAN
    exp_year: number;
    exp_month: number;
    status: 'active' | 'inactive' | 'frozen';
}
Enter fullscreen mode Exit fullscreen mode

Después de añadir el requisito de bloquear tarjetas por fraude:

{
    name: string,
    pan: number; // El número de tarjeta se llama PAN
    exp_year: number;
    exp_month: number;
    status: 'active' | 'inactive' | 'frozen' | 'blocked';
}
Enter fullscreen mode Exit fullscreen mode

Por esto creo que un enum es mucho más flexible que un campo booleano y al contrario de estos permite extender la API sin hacer breaking changes. Mi recomendación: Cada vez que vayas a representar algo utilizando un boolean, reemplázalo con un enum con 2 valores. Lo agradecerás en el futuro.

Usa objetos en lugar de valores.

Otro consejo que se me ocurre dar es que las respuestas de una API deben ser diseñadas teniendo en cuenta futuros cambios.

Una forma sencilla de hacer nuestras APIs más resistentes a cambios es utilizar objetos para representar conceptos.

Por ejemplo si junto a una tarjeta queremos devolver una lista con los identificadores de las últimas 5 transacciones mucha gente optará por un array de strings.

{
  name: string,
  pan: number; // El número de tarjeta se llama PAN
  exp_year: number;
  exp_month: number;
  status: 'active' | 'inactive' | 'frozen' | 'blocked';
  // Transaction contiene un array de IDs
  transactions: [ 
          '0000-0000',
          '0000-0001',
          '0000-0002',
  ]
}
Enter fullscreen mode Exit fullscreen mode

Ahora imaginemos que nos piden añadir el valor de cada transacción... No podemos hacerlo fácilmente sin romper el contrato de la API!

Sin embargo si desde el principio las transacciones fuesen objetos con un atributo ID

{
  name: string,
  pan: number; // El número de tarjeta se llama PAN
  exp_year: number;
  exp_month: number;
  status: 'active' | 'inactive' | 'frozen' | 'blocked';
  // Transaction contiene un array de IDs
  transactions: [ 
          {id: '0000-0000'},
          {id: '0000-0001'},
          {id: '0000-0002'},
  ]
}
Enter fullscreen mode Exit fullscreen mode

Es trivial extenderlos con tantos campos como necesitemos:

transactions: [
  { id: "0000-0000", amount: 1000 },
  { id: "0000-0001", amount: 2000 },
  { id: "0000-0002", amount: 3000 },
];
Enter fullscreen mode Exit fullscreen mode

Consulta Schema.org

La gente de Schema.org se ha preocupado de investigar y definir nombres estándar para representar conceptos en internet.

Por ejemplo para representar una Persona

Top comments (0)