En GraphQL, los tipos escalares son los tipos de datos más básicos que representan valores atómicos como Int, Float, String, Boolean y ID. Sin embargo, en muchas aplicaciones, es posible que necesitemos tipos de datos más específicos que no están cubiertos por estos tipos escalares predeterminados. Aquí es donde entran en juego los escalares personalizados.

¿Qué son los Escalares Personalizados?

Los escalares personalizados en GraphQL permiten definir tipos de datos específicos que se ajustan a las necesidades de tu aplicación. Por ejemplo, podrías necesitar un tipo de dato para representar una fecha, una URL, o un correo electrónico. Los escalares personalizados te permiten definir estos tipos y especificar cómo deben ser serializados, deserializados y validados.

Definición de un Escalar Personalizado

Para definir un escalar personalizado, necesitas:

  1. Definir el tipo escalar en tu esquema GraphQL.
  2. Implementar las funciones de serialización, deserialización y validación.

Paso 1: Definir el Tipo Escalar en el Esquema

Primero, define el tipo escalar en tu esquema GraphQL. Por ejemplo, si deseas crear un tipo escalar para representar una fecha, podrías definirlo así:

scalar Date

Paso 2: Implementar las Funciones de Serialización, Deserialización y Validación

Luego, necesitas implementar las funciones que manejarán la lógica de este tipo escalar. Aquí hay un ejemplo en JavaScript utilizando graphql-js:

const { GraphQLScalarType, Kind } = require('graphql');

const DateScalar = new GraphQLScalarType({
  name: 'Date',
  description: 'A custom scalar type for dates',
  serialize(value) {
    // Serialización: Convierte el valor a un formato que pueda ser enviado al cliente
    return value instanceof Date ? value.toISOString() : null;
  },
  parseValue(value) {
    // Deserialización: Convierte el valor recibido del cliente a un formato que pueda ser usado en el servidor
    return new Date(value);
  },
  parseLiteral(ast) {
    // Validación: Verifica que el valor recibido es del tipo esperado
    if (ast.kind === Kind.STRING) {
      return new Date(ast.value);
    }
    return null;
  }
});

module.exports = DateScalar;

En este ejemplo:

  • serialize: Convierte el valor de una fecha a una cadena ISO 8601 para enviarla al cliente.
  • parseValue: Convierte una cadena ISO 8601 recibida del cliente a un objeto Date.
  • parseLiteral: Verifica que el valor recibido es una cadena y lo convierte a un objeto Date.

Uso del Escalar Personalizado en el Esquema

Una vez que has definido e implementado tu escalar personalizado, puedes usarlo en tu esquema GraphQL como cualquier otro tipo:

type Event {
  id: ID!
  name: String!
  date: Date!
}

type Query {
  events: [Event]
}

Ejemplo Completo

A continuación, se muestra un ejemplo completo de cómo definir y usar un escalar personalizado en un servidor GraphQL utilizando graphql-js y express:

Definición del Esquema

const { ApolloServer, gql } = require('apollo-server');
const DateScalar = require('./DateScalar'); // Importa tu escalar personalizado

const typeDefs = gql`
  scalar Date

  type Event {
    id: ID!
    name: String!
    date: Date!
  }

  type Query {
    events: [Event]
  }
`;

const resolvers = {
  Date: DateScalar, // Asocia el escalar personalizado con el tipo en el esquema
  Query: {
    events: () => [
      { id: '1', name: 'GraphQL Conference', date: new Date() },
      { id: '2', name: 'JavaScript Meetup', date: new Date() }
    ]
  }
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

Ejercicio Práctico

Ejercicio: Crear un Escalar Personalizado para URLs

  1. Define un tipo escalar URL en tu esquema.
  2. Implementa las funciones de serialización, deserialización y validación para el tipo URL.
  3. Usa el tipo URL en un tipo Link que tenga campos id, description y url.

Solución:

const { GraphQLScalarType, Kind } = require('graphql');

const URLScalar = new GraphQLScalarType({
  name: 'URL',
  description: 'A custom scalar type for URLs',
  serialize(value) {
    try {
      return new URL(value).toString();
    } catch (e) {
      throw new Error('Invalid URL');
    }
  },
  parseValue(value) {
    try {
      return new URL(value).toString();
    } catch (e) {
      throw new Error('Invalid URL');
    }
  },
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      try {
        return new URL(ast.value).toString();
      } catch (e) {
        throw new Error('Invalid URL');
      }
    }
    return null;
  }
});

const typeDefs = gql`
  scalar URL

  type Link {
    id: ID!
    description: String!
    url: URL!
  }

  type Query {
    links: [Link]
  }
`;

const resolvers = {
  URL: URLScalar,
  Query: {
    links: () => [
      { id: '1', description: 'GraphQL Official Site', url: 'https://graphql.org' },
      { id: '2', description: 'Apollo GraphQL', url: 'https://www.apollographql.com' }
    ]
  }
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

Conclusión

Los escalares personalizados en GraphQL te permiten definir tipos de datos específicos que se ajustan a las necesidades de tu aplicación. Al implementar funciones de serialización, deserialización y validación, puedes asegurarte de que los datos se manejan correctamente tanto en el cliente como en el servidor. Con esta capacidad, puedes extender GraphQL para manejar una amplia variedad de tipos de datos personalizados, mejorando la flexibilidad y robustez de tus APIs.

© Copyright 2024. Todos los derechos reservados