Cómo implementar una API usando Getters y Setters en Joystick

Cómo definir una API HTTP usando getters y setters en Joystick y llamar a esos getters y setters desde su interfaz de usuario a través de los métodos get() y set() en @joystick.js/ui.

Primeros pasos

Para este tutorial, vamos a utilizar el marco JavaScript de pila completa de CheatCode, Joystick. Joystick reúne un marco de interfaz de usuario de front-end con un back-end de Node.js para crear aplicaciones.

Para comenzar, querremos instalar Joystick a través de NPM. Asegúrese de estar usando Node.js 16+ antes de instalar para garantizar la compatibilidad (lea este tutorial primero si necesita aprender a instalar Node.js o ejecutar varias versiones en su computadora):

Terminal

npm i -g @joystick.js/cli

Esto instalará Joystick globalmente en su computadora. Una vez instalado, vamos a crear un nuevo proyecto:

Terminal

joystick create app

Después de unos segundos, verá un mensaje desconectado en cd en su nuevo proyecto y ejecute joystick start :

Terminal

cd app && joystick start

Después de esto, su aplicación debería estar ejecutándose y estamos listos para comenzar.

Definición y carga de un esquema en Joystick

En una aplicación Joystick, la totalidad de su API se denomina esquema . Un esquema es un objeto JavaScript que contiene dos propiedades:getters y setters , ambos configurados en sus propios objetos.

Como sus nombres lo indican, el getters objeto contiene los puntos finales de su API para obtener datos o leer datos de una base de datos y el setters El objeto contiene los puntos finales de su API para configurar o crear, actualizar y eliminar datos.

Para comenzar, vamos a conectar un esquema básico sin definidores ni definidores y lo cargaremos en nuestra aplicación a través del node.app() función que inicia el servidor de nuestra aplicación.

/api/index.js

export default {
  getters: {},
  setters: {},
};

Queremos definir nuestro esquema en el index.js archivo bajo el /api directorio en la raíz de nuestro proyecto. Nuevamente, su esquema es solo un objeto con un getters y setters propiedad, cada conjunto a un objeto. Porque pretendemos importar esto dentro de nuestro /index.server.js a continuación, usamos un export default declaración.

/index.servidor.js

import node from "@joystick.js/node";
import api from "./api";

node.app({
  api,
  routes: {
    "/": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "*": (req, res) => {
      res.render("ui/pages/error/index.js", {
        layout: "ui/layouts/app/index.js",
        props: {
          statusCode: 404,
        },
      });
    },
  },
});

Aquí en nuestro /index.server.js archivo, hemos importado nuestro api archivar en la parte superior. Note que usamos la palabra api ya que este es el nombre del node.app() función espera que pasemos nuestro esquema como (nuevamente, api y schema se usan indistintamente y una buena frase para memorizar es "la API está definida por el esquema"). Porque hicimos un export default de vuelta en /api/index.js , aquí omitimos las llaves (utilizadas para crear named exportaciones).

En el objeto de opciones pasado a node.app() , hemos configurado api como una propiedad, utilizando la abreviatura de JavaScript para asignar automáticamente el valor de api valor que hemos importado arriba como el valor de api propiedad en nuestro node.app() objeto de opciones. Así que está claro, esto es equivalente a decir:

import node from "@joystick.js/node";
import api from "./api";

node.app({
  api: api,
  routes: { ... },
});

Eso es todo para definir nuestro esquema base y cargarlo como nuestra API. Ahora, cuando nuestra aplicación se inicie (o, en este caso, se reinicie porque ya iniciamos nuestra aplicación anteriormente), el esquema se cargará y estará disponible para solicitudes.

A continuación, vamos a construir nuestro esquema agregando un punto final de captador.

Definición de un punto final de getter

Como insinuamos anteriormente, en una aplicación Joystick, hay dos tipos de puntos finales de API:getters y setters. Los captadores son puntos finales HTTP que anticipan un HTTP GET solicitud que se les envía. Lo que hace especiales a los getters es triple:

  1. Los captadores pueden tener opcionalmente una validación de entrada para ayudarlo a validar que los valores de entrada pasados ​​a un captador cuando se llama son correctos.
  2. Cuando se les llama, a los getters se les puede pasar un output definición que le permite personalizar el valor de retorno de un captador y describe qué valores espera a cambio de la llamada.
  3. Si está disponible, los captadores obtienen acceso automáticamente a cualquier base de datos que haya cargado en su aplicación, así como al usuario que inició sesión, si existe.

Lo que es bueno es que no solo se puede acceder a esta funcionalidad a través de los métodos incorporados de Joystick para llamar captadores (los veremos más adelante), sino que también se definen como puntos finales HTTP simples como:http://localhost:2600/api/_getters/posts o http://localhost:2600/api/_getters/name-with-spaces . Esto significa que puede usar un fetch() regular función para acceder a sus captadores o acceder a sus puntos finales de API fuera de Joystick sin ningún código especial.

/api/posts/getters.js

export default {
  posts: {
    input: {},
    get: () => {
      // We'll respond to the getter request here...
    },
  },
};

Para mantener nuestra API organizada, dividiremos nuestras definiciones de getter en su propio archivo (técnicamente podríamos escribirlas directamente en nuestro esquema, pero este es un mal hábito que puede crear problemas a medida que crece nuestro esquema). Arriba, bajo nuestro /api existente carpeta en la raíz de nuestra aplicación, hemos creado otra carpeta posts y dentro de eso, un getters.js archivo.

La idea aquí es que nuestra API se compone de "recursos" o "temas". Cada recurso o tema tiene múltiples puntos finales relacionados consigo mismo. Por ejemplo, aquí, nuestro recurso es posts que tendrá puntos finales de obtención relacionados con las publicaciones y, más tarde, puntos finales de establecimiento relacionados con las publicaciones. Con este patrón, mantenemos nuestro código fácil de navegar y, lo que es más importante, fácil de mantener a largo plazo.

Tal como vimos con nuestro esquema anterior, definir un getter individual solo requiere escribir un objeto JavaScript. Aquí, export default un objeto al que se asignarán todos nuestros captadores relacionados con las publicaciones. Cada getter se define como una propiedad en ese objeto (por ejemplo, posts ) asignado a un objeto con dos propiedades propias:input y get() .

input es donde definimos la validación opcional para cualquier entrada pasada a nuestro getter cuando se llama. get() es una función en la que podemos realizar cualquier trabajo que sea necesario para responder a la solicitud del captador (es decir, obtener los datos solicitados de alguna fuente de datos). El get() La función es técnicamente abierta. Si bien normalmente nos gustaría realizar llamadas a una base de datos dentro de la función, a Joystick no le importa dónde de donde provienen sus datos, solo que los devuelve desde la función.

/api/posts/getters.js

export default {
  posts: {
    input: {
      category: {
        type: "string",
        optional: true,
      },
    },
    get: (input, context) => {
      // We'll respond to the getter request here...
    },
  },
};

Expandiendo nuestro posts getter ligeramente, ahora estamos agregando algo de validación para las entradas que anticipamos cuando se llama a nuestro getter. La validación se define utilizando la biblioteca de validación integrada de Joystick. La biblioteca toma un objeto como el que vemos que se pasa a input anterior y lo compara con el input valor que recibimos cuando se llama a nuestro getter.

En ese objeto, definimos propiedades con un nombre idéntico al nombre de la propiedad en la entrada que estamos pasando con nuestra solicitud de obtención. Por ejemplo, suponiendo que enviamos un objeto como este con nuestra solicitud:

Objeto de entrada de ejemplo

{
  category: 1234
}

Buscaríamos un category coincidente propiedad en nuestro objeto de validación, conocida como field —para ver si tiene un validator asignado a él (el nombre que usamos para el objeto asignado a las propiedades en nuestra validación). Si es así, verificamos si el valor pasado con la solicitud se ajusta a las expectativas del rules en el validador.

Lo bueno de esta validación es que se puede anidar indefinidamente para ajustarse a la estructura de su objeto de entrada . Incluso puede validar objetos anidados y matrices de objetos, lo que lo hace increíblemente flexible. Para nuestras necesidades aquí, estamos simplificando las cosas y centrándonos en un solo campo por ahora category que queremos validar tiene un valor igual a un tipo de cadena if existe (si porque el campo está marcado como opcional).

En el ejemplo anterior, observe que estamos pasando intencionalmente category como un número entero en nuestra llamada de ejemplo, no como una cadena. Esto es para señalar que cuando se llama a nuestro getter, la validación fallará y detendrá la solicitud porque la validación espera el category campo para contener una cadena, no un número entero.

/api/posts/getters.js

export default {
  posts: {
    input: {
      category: {
        type: "string",
        optional: true,
      },
    },
    get: (input, context) => {
      const query = {};

      if (input.category) {
        query.category = input.category;
      }

      return context.mongodb.collection('posts').find(query).toArray();
    },
  },
};

A continuación, con nuestro conjunto de validación, queremos conectar nuestro get() función. Recuerde, esta es la función que se llama y se espera que devuelva los datos que intentamos obtener asumiendo los datos que hemos pasado para input pasa por el paso de validación.

Nuestro get() la función toma dos argumentos:input el objeto de entrada validado pasado con la solicitud getter y context . context es un objeto que contiene algunas cosas diferentes:

  • context.req la solicitud HTTP entrante que nos proporciona la ruta Express.js como se define el getter.
  • context.res la respuesta HTTP entrante que nos proporciona la ruta Express.js como se define el getter.
  • context.user el usuario que ha iniciado sesión en la aplicación (si está disponible).
  • context.<db> donde <db> es el nombre de una de las bases de datos cargadas en su aplicación (por ejemplo, context.mongodb ).

Centrándonos en el cuerpo de nuestro get() función, recuerda:estamos definiendo un getter llamado posts así que esperamos que nuestro captador devuelva algunas publicaciones.

Para hacer eso, anticipamos que una conexión a MongoDB se define como context.mongodb (esta es la base de datos predeterminada que Joystick inicia automáticamente cuando ejecuta una aplicación recién creada con joystick start ).

Antes de hacer uso de él, primero, creamos una variable query eso actuará como la consulta "base" que queremos pasar a MongoDB (un objeto vacío significa "todo" en el lenguaje de consulta de MongoDB). Si input.category está definido (recuerde, es opcional, por lo que puede no estar presente en input ), queremos establecer la categoría aprobada en el objeto de consulta. Asumiendo que pasamos "tutorials" para input.category , esperaríamos obtener algo como esto para query :

{ category: "tutorials" }

Con nuestro query definido, a continuación, llamamos al controlador MongoDB y ejecutamos nuestra consulta. Esto puede parecer extraño. Cuando se trata de bases de datos, Joystick no hace nada más que iniciar la base de datos en su máquina local y establecer una conexión usando el controlador Node.js de esa base de datos. En otras palabras, todo después de context.mongodb aquí está "cómo funciona el controlador MongoDB en Node". Joystick no modifica esto:establece la conexión con la base de datos y la establece en context.mongodb . Eso es todo.

Lo que esperamos a cambio de esta línea es una matriz de objetos de JavaScript con cada objeto representando una publicación que está definida en la base de datos.

¡Eso es todo para definir un getter! A continuación, veremos cómo definir un setter (siguiendo un patrón casi idéntico al que aprendimos anteriormente) y luego aprenderemos cómo asignar ese setter y el getter que acabamos de definir arriba a nuestro esquema.

Definición de un punto final de establecimiento

Al igual que hicimos anteriormente, queremos dividir nuestras definiciones de setter en su propio archivo. Nuevamente, dado que estamos trabajando en el posts recurso (o "tema" si lo prefiere), nos apegaremos al /api/posts carpeta, sin embargo, esta vez, vamos a crear un /api/posts/setters.js archivo:

/api/posts/setters.js

export default {
  createPost: {
    input: {
      title: {
        type: "string",
        required: true,
      },
      category: {
        type: "string",
        required: true,
      },
      body: {
        type: "string",
        required: true,
      },
      tags: {
        type: "array",
        optional: true,
        element: {
          type: "string"
        }
      },
    },
    set: (input, context) => {
      return context.mongodb.collection('posts').insertOne({
        _id: joystick.id(),
        ...input
      });
    },
  },
};

Mismas convenciones exactas en juego. La gran diferencia aquí es que estamos usando un nombre diferente para la propiedad que configuramos en nuestro objeto exportado (para nuestro getter usamos el nombre posts , ahora estamos usando el nombre createPost para nuestro colocador) y el get() la función en el valor de esa propiedad se ha cambiado a set() .

Todo lo demás es igual en términos de comportamiento y expectativas. Técnicamente hablando, si quisiéramos, podríamos "obtener" en lugar de "establecer" algunos datos. El nombre del set() la función aquí es sugestiva pero no limitado técnicamente de ninguna manera. Los setters se comportan de manera idéntica a los getters en el sentido de que toman alguna entrada, la pasan por alguna validación (si está definida) y luego entregan ese input junto al context a una función.

Una vez más, esa función es abierta, al igual que lo fue para el get() función en nuestro getter. Puede llamar a cualquier código que desee aquí:el nombre es solo una convención para ayudar a organizar su API.

Mirando nuestra validación, la gran diferencia es que hemos agregado más campos y hemos usado el "array" escriba la regla para el tags campo. Tenga en cuenta que cuando hemos establecido type a "matriz", también podemos pasar un element campo establecido en un objeto de validación de Joystick anidado. Recuerde:la validación del joystick se puede anidar indefinidamente .

Para nuestro set() función, tal como vimos antes, estamos accediendo al controlador MongoDB asignado a context.mongodb . Esta vez, sin embargo, estamos llamando al posts insertOne de la colección método. A ese método, le estamos pasando un objeto que estamos creando, que es una combinación del input value (usamos el operador de propagación de JavaScript para "desempaquetar" el contenido en el objeto que estamos pasando a .insertOne() ) y un _id campo.

Ese campo se está configurando para una llamada a joystick.id() . Detrás de escena, Joystick expone una variable global en el servidor llamada joystick que tiene un .id() método para generar ID de cadenas hexadecimales aleatorias de n longitud (el valor predeterminado es de 16 caracteres) así:FYIlLyqzTBJdGPzz .

Eso lo hace por nuestros setters. A continuación, agreguemos nuestros getters y setters al esquema que configuramos anteriormente en el tutorial.

Asignando nuestros getters y setters al esquema

Recuerde que anteriormente definimos nuestro esquema base y lo agregamos al node.app() objeto de opciones como api . Ese esquema, sin embargo, no tenía definidos getters o setters, solo objetos vacíos para cada uno. Muy rápido, introduzcamos el /api/posts/getters.js archivo y /api/posts/setters.js archivo que acabamos de crear y configurarlos en el esquema.

/api/index.js

import postGetters from './posts/getters';
import postSetters from './posts/setters';

export default {
  getters: {
    ...postGetters,
  },
  setters: {
    ...postSetters,
  },
};

Simple. Aquí, todo lo que estamos haciendo para agregar nuestros getters y setters al esquema es importar los objetos que exportamos de cada archivo y luego, en el getters apropiado o setters objeto, utilice el operador de propagación de JavaScript ... para "desempaquetar" esos objetos en su objeto principal. Aquí, usamos una convención de nomenclatura del singular forma de nuestro nombre de recurso/tema seguido de "getters" o "setters" en mayúsculas y minúsculas.

Eso es todo. Para terminar, echemos un vistazo a cómo llamar nuestros getters y setters en la aplicación.

Llamando a getters y setters a través de @joystick.js/ui

Como un marco JavaScript de pila completa, Joystick combina nuestro front-end y back-end convenientemente en una sola aplicación. Ahora, nos alejaremos del lado del servidor de nuestra aplicación y nos centraremos en el cliente (navegador). Cuando ejecutamos joystick create anteriormente, Joystick nos dio un componente de página de ejemplo representado en el / ruta de índice de nuestra aplicación en index.server.js (Es posible que haya notado esto cuando estábamos conectando la API). Abramos ese componente de página ahora en /ui/pages/index/index.js .

/ui/pages/index/index.js

import ui from "@joystick.js/ui";
import Quote from "../../components/quote";

const Index = ui.component({
  methods: {
    handleLogHello: () => {
      console.log("Hello!");
    },
  },
  events: {
    "click .say-hello": (event, component) => {
      component.methods.handleLogHello();
    },
  },
  css: `
    div p {
      font-size: 18px;
      background: #eee;
      padding: 20px;
    }
  `,
  render: ({ component, i18n }) => {
    return `
      <div>
        <p>${i18n("quote")}</p>
        ${component(Quote, {
          quote: "Light up the darkness.",
          attribution: "Bob Marley",
        })}
      </div>
    `;
  },
});

export default Index;

Dentro de este archivo tenemos un componente de Joystick de ejemplo creado usando el @joystick.js/ui paquete (el complemento del @joystick.js/node paquete que vimos anteriormente en el servidor). @joystick.js/ui es una biblioteca para crear componentes de interfaz de usuario usando HTML puro, CSS y JavaScript.

La mayor parte del código anterior no es muy importante para nosotros en este momento. Lo que vamos a hacer ahora es modificar este componente para representar dos cosas:

  1. Un formulario para crear una nueva publicación.
  2. Una forma de mostrar publicaciones que hemos recuperado a través de nuestro posts punto final del captador.

/ui/pages/index/index.js

import ui, { get, set } from "@joystick.js/ui";

const Index = ui.component({
  state: {
    posts: [],
  },  
  lifecycle: {
    onMount: (component) => {
      component.methods.handleFetchPosts();
    },
  },
  methods: {
    handleFetchPosts: async (component) => {
      const posts = await get('posts', {
        input: {
          category: "opinion",
        },
        output: [
          'title',
          'body'
        ],
      });

      component.setState({posts});
    },
  },
  events: {
    "submit form": (event, component) => {
      event.preventDefault();

      set('createPost', {
        input: {
          title: event.target.title.value,
          category: event.target.category.value,
          body: event.target.body.value,
          tags: event.target.tags.value.split(',').map((tag) => tag.trim()),
        },
      }).then(() => {
        event.target.reset();
        component.methods.handleFetchPosts();
      });
    },
  },
  css: `
    ul {
      list-style: none;
      padding: 0;
      margin: 0 0 20px;
    }

    li {
      border: 1px solid #eee;
      padding: 20px;
    }

    li strong span {
      font-weight: normal;
      color: #aaa;
    }
  `,
  render: ({ state, each }) => {
    return `
      <div>
        <div class="posts">
          <h4>Posts</h4>
          <ul>
            ${each(state.posts, (post) => {
              return `
                <li>
                  <strong>${post.title} <span>${post.category}</span></strong>
                  <p>${post.body}</p>
                </li>
              `;
            })}
          </ul>
        </div>

        <form>
          <label for="title">Title</label><br />
          <input name="title" placeholder="title" />

          <br />

          <label for="category">Category</label><br />
          <select name="category">
            <option value="tutorials">Tutorials</option>
            <option value="opinion">Opinion</option>
            <option value="meta">Meta</option>
          </select>

          <br />

          <label for="body">Body</label><br />
          <textarea name="body"></textarea>

          <br />

          <label for="tags">Tags</label><br />
          <input name="tags" placeholder="tag1,tag2,tag3" />
          
          <br />

          <button type="submit">Create Post</button>
        </form>
      </div>
    `;
  },
});

export default Index;

Manteniendo el esqueleto del componente existente, aquí estamos intercambiando lo que se está renderizando y la funcionalidad central del componente. Esto tiene un propósito. Reutilizando el /ui/pages/index/index.js fue evitar la necesidad de conectar un nuevo componente y una ruta y mantenernos enfocados en nuestros getters y setters.

Mirando el código aquí, la parte más importante está abajo en el render() función. Al construir un componente con @joystick.js/ui , nosotros return una cadena de HTML del render() función usando acentos graves. Esto nos permite aprovechar la interpolación de cadenas de JavaScript (también conocida como "plantilla de literales") para "inyectar" dinámicamente valores en el HTML de nuestra cadena.

Detrás de escena, Joystick toma el HTML con nuestros valores inyectados y lo muestra en el navegador. En nuestro código aquí, para demostrar nuestros getters y setters en acción, queremos representar dos cosas:una lista de publicaciones existentes (recuperadas de nuestro getter) de la base de datos y un formulario para agregar nuevas publicaciones (cuyo contenido se transmite a través de nuestro setter ).

Debido a que aún no tenemos ninguna publicación en nuestra base de datos, a continuación, queremos ver el events propiedad establecida en el objeto que estamos pasando a ui.component() . Aquí es donde definimos los eventos DOM de JavaScript en un componente Joystick. Cada evento que queremos escuchar se asigna al objeto que pasamos a events. Creamos un oyente configurando la clave o el nombre de la propiedad como una cadena que contiene primero el tipo de evento DOM que queremos escuchar (en nuestro ejemplo, submit ) y el elemento que queremos escuchar para ese evento on (en nuestro ejemplo, form ).

A esa propiedad, le asignamos una función que se llama cada vez que ocurre ese evento en el navegador/DOM. Para nuestro ejemplo, queremos llamar a nuestro setter createPost en el servidor siempre que tenga lugar este evento. Para llamarlo, arriba hemos añadido un named import (indicado por las llaves) para el set() función que está incluida en @joystick.js/ui . Esta es una función contenedora de JavaScript fetch() método integrado en los navegadores para realizar solicitudes HTTP.

Nos brinda una API simple para realizar nuestra solicitud. Toma el nombre del setter que queremos llamar como una cadena para su primer argumento, seguido de un objeto de opciones. En ese objeto de opciones, aquí, estamos pasando los valores de nuestro formulario. Hacemos esto accediendo al DOM event objeto pasado a nuestra función por Joystick.

Debido a que Joystick nos da acceso al evento DOM nativo, podemos acceder al valor de nuestras entradas directamente diciendo event.target.<field>.value donde event.target se refiere al <form></form> elemento donde se recibió el evento de envío y <field>.value es igual al valor de la entrada con un name atributo igual a <field> en nuestro HTML renderizado.

Eso está claro, si tuviéramos una entrada como <input name="pizza" /> en nuestro HTML, escribiríamos algo como event.target.pizza.value .

Con eso, nuestro colocador está listo para ser llamado. Recuerde:todo lo que estamos haciendo en el servidor es entregar la entrada validada a MongoDB para ponerla en nuestro posts colección en la base de datos.

Después de llamar a nuestro setter, estamos listos para pasar al siguiente paso:obtener nuestras publicaciones de la base de datos.

Porque esperamos el set() método importado de @joystick.js/ui para devolver una promesa de JavaScript, al final de nuestra llamada a esa función encadenamos un .then() método, pasando una función de devolución de llamada que nos gustaría ejecutar después de que se complete la solicitud del setter.

Dentro llamamos al .reset() en nuestro formulario (reutilizando el event.target pasado a nuestro detector de eventos DOM) para borrar el campo y luego llamar a un método personalizado definido en nuestro componente handleFetchPosts() . Podemos acceder a esto porque todos los detectores de eventos DOM definidos en el events objeto de un componente recibe el DOM event como primer argumento y el component completo instancia como segundo argumento.

/ui/pages/index/index.js

import ui, { get, set } from "@joystick.js/ui";

const Index = ui.component({
  state: {
    posts: [],
  },  
  lifecycle: {
    onMount: (component) => {
      component.methods.handleFetchPosts();
    },
  },
  methods: {
    handleFetchPosts: async (component) => {
      const posts = await get('posts', {
        input: {
          category: "opinion",
        },
        output: [
          'title',
          'body'
        ],
      });

      component.setState({ posts });
    },
  },
  events: {
    "submit form": (event, component) => {
      event.preventDefault();

      set('createPost', { ... }).then(() => {
        document.querySelector('form').reset();
        component.methods.handleFetchPosts();
      });
    },
  },
  css: `...`,
  render: ({ state, each }) => {
    return `
      <div>
        <div class="posts">
          <h4>Posts</h4>
          <ul>
            ${each(state.posts, (post) => {
              return `
                <li>
                  <strong>${post.title} <span>${post.category}</span></strong>
                  <p>${post.body}</p>
                </li>
              `;
            })}
          </ul>
        </div>

        <form>
          ...
          <button type="submit">Create Post</button>
        </form>
      </div>
    `;
  },
});

export default Index;

El methods El objeto asignado a un componente Joystick contiene funciones misceláneas que queremos llamar en relación con nuestro componente. Se puede acceder a estos métodos desde cualquier parte de nuestro componente a través de component instancia (pasada a todas las funciones en un componente Joystick).

Como acabamos de ver en el .then() devolución de llamada de nuestro set() llame al events , podemos llamar a un método directamente escribiendo component.methods.<methodName> .

Para nuestras necesidades, queremos conectar un método que llame a nuestro posts getter en el servidor y recupera nuestros datos. Similar a cómo llamamos a nuestro setter a través de set() , un método hermano para captadores es también exportado desde @joystick.js/ui llamado get() .

Como era de esperar, get() toma el nombre del captador que queremos llamar como una cadena para su primer argumento y luego un objeto de opciones como su segundo argumento. Recuerde que antes, cuando conectamos nuestro punto final de getter, anticipamos un posible category valor que se pasa para nuestra entrada. En nuestro ejemplo aquí, estamos pasando "opinion" como nombre de categoría para decir "cuando ejecute este getter, solo devuelva publicaciones con un category campo igual a opinion ."

Si miramos hacia abajo en nuestro render() función, podemos usar una de tres categorías aquí:tutorials , meta , o opinion .

Además de nuestro input , una característica única de getters y setters en Joystick es el output opción (conocida como SelectiveFetch en Joystick). La salida le permite pasar campos específicos en un objeto o matriz de objetos devueltos por un captador para personalizar su salida. Esto hace posible reutilizar un captador en varios lugares, adaptando la salida a las necesidades de su interfaz de usuario. Para demostrar esto aquí, estamos pasando dos de los cuatro campos definidos en cada una de nuestras publicaciones title y body .

En el servidor, antes de que Joystick envíe nuestros datos, los pasará a través del output matriz y elimine cualquier dato que no haya solicitado. Aquí, porque pasamos title y body en nuestra matriz, decimos "en cada objeto de publicación, solo devuélveme el título y el cuerpo de la publicación, descartando todo lo demás". Si comentamos el output desactive la opción y vuelva a ejecutar nuestro getter, veremos que all los campos se muestran en lugar de solo lo que pasamos en output .

Al igual que con el set() método que vimos anteriormente, esperamos get() para devolver una promesa de JavaScript. Para mostrar una forma diferente de manejar la respuesta de get() (podría usar el mismo patrón con set() ), usamos el patrón JavaScript async/await para omitir la necesidad de un .then() devolución de llamada.

Esto funciona asignando la palabra clave async a la función principal donde await se va a utilizar la palabra clave. Ponemos el await palabra clave aquí delante de nuestra llamada a get() para decir "espere hasta que este getter responda y luego asigne el valor de respuesta a la variable posts .

Con esa variable (suponiendo que contiene una serie de publicaciones como las que devolvimos de nuestro captador), tomamos el component instancia (para methods , esto se pasa automáticamente como el último argumento posible; en este caso, es el primero porque no estamos pasando ningún otro argumento a handleFetchPosts() cuando lo llamamos) y utilizamos su .setState() método para establecer el state.posts valor en el componente a la matriz que recibimos de nuestro getter.

Podemos ver que el state el objeto en la parte superior de nuestro componente recibe un posts predeterminado valor establecido en una matriz vacía. Cuando se ejecuta nuestro getter, suponiendo que devolvamos datos de nuestro getter, completaremos automáticamente esta matriz. Abajo en nuestro render() método, usamos el each() función render en Joystick para decir "para cada una de las publicaciones en state.posts , llame a esta función que representa una cadena de HTML y recibe la publicación actual (o el valor) que estamos recorriendo".

A su vez, esperamos un <li></li> etiqueta que se representará en la pantalla con el title de cada publicación , category y body inyectado en él.

Una última nota antes de que hagamos una prueba. Tenga en cuenta que también hemos agregado un objeto lifecycle a nuestro componente. Este objeto nos permite definir funciones que se llaman en diferentes etapas del "ciclo de vida" de nuestro componente, o "lo que el componente está haciendo actualmente". Hay tres métodos de ciclo de vida en Joystick:onBeforeMount , onMount y onBeforeUnmount .

Aquí, estamos usando onMount para decir "cuando este componente se renderice, llame al handleFetchPosts() método en nuestro methods objeto." Lo que esperamos es que cuando nuestro componente se represente en la pantalla por primera vez, irá a buscar cualquier publicación existente y la pondrá en estado, desencadenando una nueva representación en nuestro HTML y mostrando la lista de publicaciones en la pantalla. Cada vez que agregamos una nueva publicación, también esperamos el mismo comportamiento (lo que significa que las publicaciones aparecen en la pantalla tan pronto como se agregan a la base de datos).

¡Eso es todo! Hagamos una prueba y veamos cómo funciona.

Terminando

En este tutorial, aprendimos cómo crear una API simple utilizando los captadores y definidores de Joystick. Aprendimos a crear un getter y setter y luego cargarlos en nuestro esquema y adjuntar ese esquema a nuestra aplicación como nuestra API. También aprendimos cómo llamar a getters y setters en el navegador, usando el get() y set() métodos incluidos en el @joystick.js/ui biblioteca.