Scalars, filtros y queries avanzadas

4/23
Recursos

Aportes 12

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Al ver un curso espero aprender cosas no a escuchar explicaciones de codigos y/o proyectos que ya estan todos desarrollados… Cual seria el objetivo de este curso si solo me pongo a leer codigo
Para otros cursos mejor ponte a programar desde cero y explicar, no te pongas a explicar codigo que ya esta escrito 😃

Me gusta mucho la metodología para enseñar que usa este profesor.
Es dificil, pero genera un gran reto en cada clase

Me perdonaran pero que sentido tiene venir a una clase a leer el 30% del total del código agregado y ver al profesor copiar y pegar, no es que no quiera tomarme el tiempo de entender pero se trata de una clase

Por si alguien se pregunta de dónde sacó el profesor la definición del scalar (al menos yo no la sabía, ni la entendía), la tomó de esta documentación de Apollo, creo que esta otra docu de GraphQL también les resultará útil.

Después de 3 días aprendiendo TypeScript corregí mi código basado en esta clase 😄 😄
Lo explicare paso a paso:


  • Dividí el código en una carpeta models la cual tiene el archivo schema.graphql y una resolvers con los resolvers.

  • El index.ts configure el context del apollo server para pasar la “db” (luego tendra sentido)

  • El schema.graphql tiene el modelo de la clase:

  • Dentro de resolvers/index.ts cree el objeto para el resolver de apollo server

  • Para configurar los queries y mutations cree sus propios archivos y el objeto context tiene toda la información de la “db” que configure en la propiedad context del apollo server (ignoren el type de la línea 3 xd)

  • Dentro de /resolvers/base estan algunos tipos usados en los queries y mutations y el resolver para el type Avocado o algún otro type si llegara a crear otro

El unico problema que tuve fue este warning:

Fue muy complejo pero divertido programar todo eso, aprendi muchas cosas de TypeScript como los Utility Types, keyof, etc. Y muchas otras formas de configurar el resolver.
Repo de mi proyecto


Nunca pares de aprender

Les comparto el código del Mutation 15:40 -> Data completa del repo del profe

{
  "data": {
    "name": "Reed Avocado",
    "price": 1.18,
    "image": "/images/reed.jpg",
      "description":
        "Developed from a chance seedling found in 1948 by James S. Reed in California, this cultivar has large, round, green fruit with a smooth texture and dark, thick, glossy skin. Smooth and delicate, the flesh has a slightly nutty flavor. The skin ripens green. A Guatemalan type, it is hardy to −1 °C (30 °F). Tree size is about 5 by 4 m (16.4 by 13.1 ft)",
      "shape": "Round",
      "hardiness": "−1 °C",
      "taste": "Exquisite, is an avocado"
  }
}

Mi repo. Le agregare un tag de un antes y después de esta clase a ver que tal queda luego 😄
https://github.com/MiguelHG2351/platzi-node-prisma-graphql

Estructura del proyecto

🛠 Repositorio Link
.

.
En mi caso, sigo la estructura de mi proyecto, dandole al directorio GraphQL la siguiente estructura:

graphql
 └─⫸ enums
 └─⫸ models
	 └─⫸ Account
		 └─⫸ Account.entity.ts
		 └─⫸ Account.model.ts
		 └─⫸ Account.schema.ts
 └─⫸ scalars
 └─⫸ index.ts

Donde en cada elemento:

// Account.entity.ts
export interface Account {
    id: number
    email: string
    password: string
}

export type Query = Pick<Account, 'id'>
export type Payload = Pick<Account, 'email' | 'password'>
// Account.model.ts
import Model from '@models/Model'

import { Account, Query, Payload } from './Account.entity'

import accounts from '@db/mocks/account.mock'

export default class AccountModel extends Model<Account, Query, Payload> {
    /**
     * @description Find an account by id.
     * @param {Query} {id} Account ID.
     * @returns {Account | undefined} Account */
    findUnique({ id }: Query): Account | undefined {
        return accounts.find((account) => account.id === id)
    }
    /**
     * @description Find pool of accounts.
     * @returns {Account[]} Array of accounts. */
    findMany(): Account[] {
        return accounts
    }
    /**
     * @description Create an account.
     * @returns {Account} */
    create(payload: Payload): Account {
        return {
            id: 1,
            ...payload,
        }
    }
}

// Account.schema.ts
import { gql } from 'apollo-server'

import { Query, Payload } from './Account.entity'
import AccountModel from './Account.model'

import Error from '@controllers/Error.controller'

const accountModel = new AccountModel()

export default {
    Schema: gql`
        type Account {
            id: ID!
            email: String!
            password: String!
        }

        input CreateAccount {
            email: String!
            password: String!
        }

        extend type Query {
            account(id: ID!): Account!
            accounts: [Account!]!
        }
    `,
    Query: {
        account: (_: any, query: Query) => {
            const account = accountModel.findUnique(query)
            if (!account) throw new Error('NOT_FOUND')
            return account
        },
        accounts: () => {
            return accountModel.findMany()
        },
    },
    Mutation: {
        createAccount: (_: any, payload: Payload) => {
            return accountModel.create(payload)
        },
    },
}

Schema Definition Language (SDL)

Especificación que define un schema y definirlo como una cadena.
.
Un schema define una colección de tipos y sus relaciones a través de types.
.
Cada relación define una unificación, lo que permite que los clientes puedan ver la información disponible, organizada.
.

💡 Un SDL no es responsable de la localidad real de la información y como es almacenada, es decir, se mantiene agnóstica.

.

Esquemas y tipos

Dado el siguiente código:

type Character {
  name: String!
  appearsIn: [Episode!]!
}

Character representa un Object Type representando un modelo. Dicho modelo pose fields como name y appearsIn.
.
Cada atributo cuenta con una tipo scalar , nulleable o requerido !.
.
Los tipos Query son estructuras que definen puntos de entradas para operaciones de lectura.

type Query {
  books: [Book]
  authors: [Author]
}

Los tipos Mutation son estructuras que definen puntos de entradas para operaciones de escritura.

type Mutation {
  addBook(title: String, author: String): Book
}

Los tipos Input son estructuras que definen una jerarquía de información para argumentos.
.

input BlogPostContent {
  title: String
  body: String
  media: [MediaDetails!]
}

input MediaDetails {
  format: MediaFormat!
  url: String!
}

enum MediaFormat {
  IMAGE
  VIDEO
}

Uniones e Interfaces

Las uniones son definiciones de tipos utilizados para campos o lista de campos.

union SearchResult = Book | Author

type Book {
  title: String!
}

type Author {
  name: String!
}

type Query {

  search(contains: String): [SearchResult!]
}

Una interfaz especifica un conjunto de campos que múltiples objetos pueden incluir.

interface Book {
  title: String!
  author: Author!
}

type Textbook implements Book {
  title: String! # Must be present
  author: Author! # Must be present
  courses: [Course!]!
}

type Query {
  books: [Book!] # Can include Textbook objects
}

Si un objeto implementa una interface, este debe incluir todos los campos de la interface.
.
Cuando se ocupa una interface como tipo de un capo, este puede incluir todos los objetos que implementan la interfaz.

Escalares

Por default, GraphQL incluye escalares por default tales como Int , Float, String, Boolean y ID.
.
Para definir un escalar, se necesita agregar al esquema la definición scalar Date.
.
Después se necesita determinar sus interacciones mediante una instancia de la clase GraphQLScalarType . De esta instancia, en particular, se necesita la representación del valor y serialized/deserialized.

import dayjs from 'dayjs'
import { GraphQLScalarType, Kind } from 'graphql'

export default new GraphQLScalarType({
    name: 'Date',
    description: 'Date custom scalar type',
    serialize: (date) => dayjs(date as string).toJSON(),
    parseValue: (date) => dayjs(date as number),
    parseLiteral: (ast) =>
        ast.kind === Kind.INT ? dayjs(parseInt(ast.value, 10)) : null,
})

.
Cada inicialización define los siguientes métodos:

  • Serialize - método que convierte una representación de valor a un formato compatible en JSON
    .
  • parseValue - método que convierte un valor en JSON a una representación de valor enviado como args
    .
  • parseLiteral - método que convierte un valor en Abstract Syntax Tree (AST) a una representación de valor.

Para poder sacarle provecho a este curso hay que tener un conocimiento basico de typescript, apollo, prisma y graphql porque no te lleva paso a paso mientras crea el paso sino que te muestra codigo que ya existe

Vengo del futuro, si no les funciona la query para obtener un avocado, pueden usar algo como esto:

type GetOneAvocadoArgs = {
  id: string
}

const getOneAvocado = (
  parent: unknown,
  { id }: GetOneAvocadoArgs,
  context: unknown
): Avocado | null => {
  const avocado = avocados.find(avo => avo.id === id)

  return avocado ?? null
}