Repensar Twitter como una aplicación sin servidor
En un artículo anterior, mostramos cómo crear una API GraphQL con FaunaDB. También hemos escrito una serie de artículos [ 1 , 2 , 3 , 4 ] que explican cómo las bases de datos tradicionales creadas para la escalabilidad global tienen que adoptar una consistencia eventual (frente a una fuerte) y/o hacer concesiones en las relaciones y posibilidades de indexación. FaunaDB es diferente ya que no tiene estos compromisos. Está diseñado a escala para que pueda servir de manera segura a su futura startup sin importar cuán grande sea, sin sacrificar las relaciones y los datos consistentes.
En este artículo, estamos muy emocionados de comenzar a reunir todo esto en una aplicación del mundo real con datos altamente dinámicos sin servidor utilizando React Hooks, FaunaDB y Cloudinary. Usaremos Fauna Query Language (FQL) en lugar de GraphQL y comenzaremos con un enfoque solo de interfaz que accederá directamente a la base de datos sin servidor FaunaDB para el almacenamiento, autenticación y autorización de datos.
El estándar de oro, por ejemplo, las aplicaciones que cuentan con una tecnología específica, es una aplicación de tareas pendientes, principalmente porque son simples. Cualquier base de datos que exista puede servir para una aplicación muy simple y brillar.
¡Y es exactamente por eso que esta aplicación será diferente! Si realmente queremos mostrar cómo FaunaDB sobresale en aplicaciones del mundo real, entonces necesitamos construir algo más avanzado.
Presentamos Fwitter
Cuando empezamos en Twitter, las bases de datos eran malas. Cuando nos fuimos, todavía estaban mal.
Evan Weaver
Dado que FaunaDB fue desarrollado por ex ingenieros de Twitter que experimentaron estas limitaciones de primera mano, una aplicación similar a Twitter parecía una elección apropiadamente sentimental. Y, dado que lo estamos construyendo con FaunaDB, llamemos a este bebé sin servidor ' Fwitter' .
A continuación se muestra un breve vídeo que muestra cómo se ve y el código fuente completo está disponible en GitHub.
Cuando clonas el repositorio y comienzas a investigar, es posible que notes una gran cantidad de consultas de ejemplo bien comentadas que no se tratan en este artículo. Esto se debe a que usaremos Fwitter como nuestra aplicación de ejemplo en artículos futuros y le incorporaremos funciones adicionales con el tiempo.
Pero, por ahora, aquí hay un resumen básico de lo que cubriremos aquí:
- Modelando los datos
- Configurar el proyecto
- Creando la interfaz
- El controlador JavaScript de FaunaDB
- Creando datos
- Proteger sus datos con UDF y roles ABAC
- Cómo implementar la autenticación
- Agregar Cloudinary para medios
- Recuperando datos
- Más en el código base
Desarrollamos estas funciones sin tener que configurar operaciones o configurar servidores para su base de datos. Dado que tanto Cloudinary como FaunaDB son escalables y se distribuyen de forma inmediata, nunca tendremos que preocuparnos por configurar servidores en múltiples regiones para lograr bajas latencias para los usuarios de otros países.
¡Vamos a sumergirnos!
Modelando los datos
Antes de que podamos mostrar cómo FaunaDB sobresale en las relaciones, debemos cubrir los tipos de relaciones en el modelo de datos de nuestra aplicación. Las entidades de datos de FaunaDB se almacenan en documentos, que luego se almacenan en colecciones, como filas en tablas. Por ejemplo, los detalles de cada usuario estarán representados por un documento de Usuario almacenado en una colección de Usuarios. Y eventualmente planeamos admitir métodos de inicio de sesión único y de inicio de sesión basados en contraseña para un solo usuario, cada uno de los cuales se representará como un documento de Cuenta en una colección de Cuentas.
En este punto, un usuario tiene una cuenta, por lo que no importa qué entidad almacena la referencia (es decir, el ID de usuario). Podríamos haber almacenado la ID de usuario en la Cuenta o en el documento de Usuario en una relación uno a uno:
Sin embargo, dado que un usuario eventualmente tendrá varias cuentas (o métodos de autenticación), tendremos un modelo de uno a muchos.
En una relación de uno a muchos entre Usuarios y Cuentas, cada Cuenta apunta a un solo usuario, por lo que tiene sentido almacenar la referencia del Usuario en la Cuenta:
También tenemos relaciones de muchos a muchos, como las relaciones entre Fweets y Usuarios, debido a las complejas formas en que los usuarios interactúan entre sí a través de Me gusta, comentarios y Refweets.
Además, utilizaremos una tercera colección, Fweetstats, para almacenar información sobre la interacción entre un Usuario y un Fweet.
Los datos de Fweetstats nos ayudarán a determinar, por ejemplo, si colorear o no los iconos que indican al usuario que ya le ha gustado, comentado o retuiteado un Fweet. También nos ayuda a determinar qué significa hacer clic en el corazón: diferente o similar.
El modelo final de la aplicación se verá así:
Los Fweets son el centro del modelo, porque contienen los datos más importantes del Fweet como la información sobre el mensaje, la cantidad de me gusta, refweets, comentarios y los medios de Cloudinary que se adjuntaron. FaunaDB almacena estos datos en un formato json similar a este:
Como se muestra en el modelo y en este ejemplo json, los hashtags se almacenan como una lista de referencias. Si quisiéramos, podríamos haber almacenado el hashtag json completo aquí, y esa es la solución preferida en bases de datos basadas en documentos más limitadas que carecen de relaciones. Sin embargo, eso significaría que nuestros hashtags estarían duplicados en todas partes (como lo están en bases de datos más limitadas) y sería más difícil buscar hashtags y/o recuperar Fweets para un hashtag específico como se muestra a continuación.
Tenga en cuenta que un Fweet no contiene un enlace a Comentarios, pero la colección de Comentarios contiene una referencia al Fweet. Esto se debe a que un comentario pertenece a un Fweet, pero un Fweet puede tener muchos comentarios, similar a la relación de uno a muchos entre Usuarios y Cuentas.
Finalmente, hay una colección FollowerStats que básicamente guarda información sobre cuánto interactúan los usuarios entre sí para personalizar sus respectivos feeds. No cubriremos mucho en este artículo, pero puede experimentar con las consultas en el código fuente y permanecer atento a un artículo futuro sobre indexación avanzada.
Con suerte, estás empezando a ver por qué elegimos algo más complejo que una aplicación de tareas pendientes. Aunque Fwitter no se acerca a la complejidad de la aplicación real de Twitter en la que se basa, ya se está haciendo evidente que implementar una aplicación de este tipo sin relaciones sería una seria ruptura de ideas.
Ahora, si aún no lo has hecho desde el repositorio de github, ¡finalmente es hora de ejecutar nuestro proyecto localmente!
Configurar el proyecto
Para configurar el proyecto, vaya al panel de FaunaDB y regístrese. Una vez que esté en el panel, haga clic en Nueva base de datos , complete un nombre y haga clic en Guardar . Ahora debería estar en la página "Descripción general" de su nueva base de datos.
A continuación, necesitamos una clave que usaremos en nuestros scripts de configuración. Haga clic en la pestaña Seguridad en la barra lateral izquierda, luego haga clic en el botón Nueva clave .
En el formulario “Nueva clave”, la base de datos actual ya debería estar seleccionada. Para "Rol", déjelo como "Administrador". Opcionalmente, agregue un nombre de clave. A continuación, haga clic en Guardar y copie la clave secreta que se muestra en la página siguiente. No se volverá a mostrar.
Ahora que tiene su base de datos secreta, clone el repositorio de git y siga el archivo Léame. Hemos preparado algunos scripts para que solo tenga que ejecutar los siguientes comandos para inicializar su aplicación, crear todas las colecciones y completar su base de datos. Los guiones le darán más instrucciones:
// install node modulesnpm install// run setup, this will create all the resources in your database// provide the admin key when the script asks for it. // !!! the setup script will give you another key, this is a key// with almost no permissions that you need to place in your .env.local as the// script suggestionsnpm run setupnpm run populate // start the frontend
Después del script, su archivo .env.local debe contener la clave de arranque que le proporcionó el script (no la clave de administrador)
REACT_APP_LOCAL___BOOTSTRAP_FAUNADB_KEY=bootstrap key
Opcionalmente, puedes crear una cuenta con Cloudinary y agregar tu nombre de nube y una plantilla pública (hay una plantilla predeterminada llamada 'ml_default' que puedes hacer pública) al entorno para incluir imágenes y videos en los fweets.
REACT_APP_LOCAL___CLOUDINARY_CLOUDNAME=cloudinary cloudnameREACT_APP_LOCAL___CLOUDINARY_TEMPLATE=cloudinary template
Sin estas variables, el botón de incluir medios no funcionará, pero el resto de la aplicación debería funcionar bien:
Creando la interfaz
Para la interfaz, utilizamos Create React App para generar una aplicación y luego dividimos la aplicación en páginas y componentes. Las páginas son componentes de nivel superior que tienen sus propias URL. Las páginas de inicio de sesión y registro hablan por sí solas. Inicio es el feed estándar de Fweets de los autores que seguimos; esta es la página que vemos cuando iniciamos sesión en nuestra cuenta. Y las páginas Usuario y Etiqueta muestran los Fweets de un usuario o etiqueta específicos en orden cronológico inverso.
Usamos React Router para dirigir a estas páginas dependiendo de la URL, como puede ver en el src/app.js
archivo.
Router SessionProvider value={{ state, dispatch }} Layout Switch Route exact path="/accounts/login" Login / /Route Route exact path="/accounts/register" Register / /Route Route path="/users/:authorHandle" component={User} / Route path="/tags/:tag" component={Tag} / Route path="/" Home / /Route /Switch /Layout /SessionProvider/Router
La única otra cosa a tener en cuenta en el fragmento anterior es SessionProvider, que es un contexto de React para almacenar la información del usuario al iniciar sesión. Revisaremos esto en la sección de autenticación. Por ahora, basta con saber que esto nos da acceso a la información de la Cuenta (y por tanto del Usuario) de cada componente.
Eche un vistazo rápido a la página de inicio ( src/pages/home.js
) para ver cómo utilizamos una combinación de enlaces para administrar nuestros datos. La mayor parte de la lógica de nuestra aplicación se implementa en consultas de FaunaDB que se encuentran en la src/fauna/querie
carpeta s. Todas las llamadas a la base de datos pasan a través del administrador de consultas, que en un artículo futuro refactorizaremos en llamadas a funciones sin servidor. Pero por ahora estas llamadas se originan desde el frontend y protegeremos las partes sensibles con las reglas de seguridad ABAC y las funciones definidas por el usuario (UDF) de FaunaDB. Dado que FaunaDB se comporta como una API protegida por token, no tenemos que preocuparnos por un límite en la cantidad de conexiones como lo haríamos en las bases de datos tradicionales.
El controlador JavaScript de FaunaDB
A continuación, eche un vistazo al src/fauna/query-manager.js
archivo para ver cómo conectamos FaunaDB a nuestra aplicación usando el controlador JavaScript de FaunaDB, que es solo un módulo de nodo que extrajimos con `npm install`. Como con cualquier módulo de nodo, lo importamos a nuestra aplicación de la siguiente manera:
import faunadb from 'faunadb'
Y cree un cliente proporcionando un token.
this.client = new faunadb.Client({ secret: token || this.bootstrapToken})
Cubriremos los tokens un poco más en la sección Autenticación. Por ahora, ¡creemos algunos datos!
Creando datos
La lógica para crear un nuevo documento Fweet se puede encontrar en el src/fauna/queries/fweets.js
archivo. Los documentos de FaunaDB son como JSON y cada Fweet sigue la misma estructura básica:
const data = { data: { message: message, likes: 0, refweets: 0, comments: 0, created: Now() }}
La Now()
función se utiliza para insertar la hora de la consulta para que los Fweets en el feed de un usuario se puedan ordenar cronológicamente. Tenga en cuenta que FaunaDB coloca automáticamente marcas de tiempo en cada entidad de la base de datos para consultas temporales. Sin embargo, la marca de tiempo de FaunaDB representa la hora en que se actualizó por última vez el documento, no la hora en que se creó, y el documento se actualiza cada vez que se le da me gusta a un Fweet; para nuestro orden de clasificación previsto, necesitamos el tiempo creado.
A continuación, enviamos estos datos a FaunaDB con la función Create() . Al proporcionar Create()
la referencia a la colección Fweets mediante Collection(‘fweets’)
, especificamos dónde deben ir los datos.
const query = Create(Collection('fweets'), data )
Ahora podemos encapsular esta consulta en una función que toma un parámetro de mensaje y lo ejecuta mediante client.query()
el cual enviará la consulta a la base de datos. Solo cuando llamemos client.query()
se enviará la consulta a la base de datos y se ejecutará. Antes de eso, combinamos tantas funciones FQL como queramos para construir nuestra consulta.
function createFweet(message, hashtags) { const data = … const query = … return client.query(query)}
Tenga en cuenta que hemos utilizado variables de JavaScript antiguas y simples para redactar esta consulta y, en esencia, simplemente hemos llamado funciones. Escribir FQL tiene que ver con la composición de funciones; las consultas se construyen combinando funciones pequeñas en expresiones más grandes. Este enfoque funcional tiene ventajas muy importantes. Nos permite utilizar funciones del lenguaje nativo, como variables de JavaScript, para redactar consultas y, al mismo tiempo, escribir funciones FQL de orden superior que están protegidas contra inyecciones.
Por ejemplo, en la consulta siguiente, agregamos hashtags al documento con una CreateHashtags()
función que hemos definido en otra parte usando FQL.
const data = { data: { // ... hashtags: CreateHashtags(tags), likes: 0, // ... }
La forma en que FQL funciona desde el lenguaje anfitrión del controlador (en este caso, JavaScript) es lo que hace que FQL sea un eDSL (lenguaje integrado específico de dominio). Funciones como CreateHashtags()
se comportan como una función FQL nativa en el sentido de que ambas son solo funciones que reciben información. Esto significa que podemos ampliar fácilmente el lenguaje con nuestras propias funciones, como en esta biblioteca FQL de código abierto de la comunidad Fauna.
También es importante notar que creamos dos entidades en dos colecciones diferentes, en una transacción. Por lo tanto, si las cosas van mal, no hay riesgo de que se cree el Fweet pero no los Hashtags. En términos más técnicos, FaunaDB es transaccional y consistente ya sea que ejecute consultas en múltiples colecciones o no, una propiedad que es poco común en bases de datos distribuidas escalables.
A continuación, debemos agregar el autor a la consulta. Primero, podemos usar la función FQL para devolver una referencia al documento actualmente conectado. Como se analizó anteriormente en la sección de modelado de datos, ese documento es del tipo Cuenta y está separado de Usuarios para admitir SSO en una fase posterior. Identity()
Luego, debemos incluir Identity()
un Get()
para acceder al documento de Cuenta completo y no solo a la referencia al mismo.
Get(Identity())
Finalmente, envolvemos todo eso en un Select()
para seleccionar el data.user
campo del documento de cuenta y lo agregamos al JSON de datos.
const data = { data: { // ... hashtags: CreateHashtags(tags), author: Select(['data', 'user'], Get(Identity())), likes: 0, // ... }}
Ahora que hemos construido la consulta, juntemos todo y llamemos client.query(query)
para ejecutarla.
function createFweet(message, hashtags) { const data = { data: { message: message, likes: 0, refweets: 0, comments: 0, author: Select(['data', 'user'], Get(Identity())), hashtags: CreateHashtags(tags), created: Now() } } const query = Create(Collection('fweets'), data ) return client.query(query)}
Al utilizar la composición funcional, puede combinar fácilmente toda su lógica avanzada en una consulta que se ejecutará en una transacción. Consulte el archivo src/fauna/queries/fweets.js
para ver el resultado final que aprovecha aún más la composición de funciones para agregar limitación de velocidad, etc.
Proteger sus datos con UDF y roles ABAC
El lector atento ya tendrá algunas ideas sobre la seguridad. Básicamente, estamos creando consultas en JavaScript y llamando a estas consultas desde la interfaz. ¿Qué impide que un usuario malintencionado modifique estas consultas?
FaunaDB proporciona dos características que nos permiten proteger nuestros datos: Control de acceso basado en atributos (ABAC) y Funciones definidas por el usuario (UDF). Con ABAC, podemos controlar a qué colecciones o entidades puede acceder una clave o token específico escribiendo Roles.
Con las UDF, podemos enviar declaraciones FQL a la base de datos mediante el uso de CreateFunction()
.
CreateFunction({ name: 'create_fweet', body: your FQL statement, })
Una vez que la función está en la base de datos como una UDF, donde la aplicación ya no puede modificarla, llamamos a esta UDF desde el front-end.
client.query( Call(Function('create_fweet'), message, hashTags))
Dado que la consulta ahora se guarda en la base de datos (como un procedimiento almacenado), el usuario ya no puede manipularla.
Un ejemplo de cómo se pueden utilizar las UDF para asegurar una llamada es que no pasamos el autor del Fweet. En cambio, el autor del Fweet se deriva de la función Identity(), lo que hace imposible que un usuario escriba un Fweet en nombre de alguien.
Por supuesto, todavía tenemos que definir que el usuario tiene acceso para llamar a la UDF. Para eso, usaremos un rol ABAC muy simple que define un grupo de miembros del rol y sus privilegios. Esta función se denominará logged_in_role
, su membresía incluirá todos los documentos de la colección de Cuentas y a todos estos miembros se les otorgará el privilegio de llamar a la create_fweet
UDF.
CreateRole( name: 'logged_in_role', privileges: [ { resource: q.Function('create_fweet'), actions: { call: true } } ], membership: [{ resource: Collection('accounts') }],)
Ahora sabemos que estos privilegios se otorgan a una cuenta, pero ¿cómo nos 'convertimos' en una Cuenta? Utilizando la función FaunaDB Login() para autenticar a nuestros usuarios como se explica en la siguiente sección.
Cómo implementar la autenticación en FaunaDB
Acabamos de mostrar un rol que otorga a Cuentas los permisos para llamar a la create_fweets
función. Pero ¿cómo nos “convertimos” en una Cuenta?
Primero, creamos un nuevo documento de Cuenta, almacenando las credenciales junto con cualquier otro dato asociado con la Cuenta (en este caso, la dirección de correo electrónico y la referencia al Usuario).
return Create(Collection('accounts'), { credentials: { password: password }, data: { email: email, user: Select(['ref'], Var('user')) } })}
Luego podemos llamar Login()
a la referencia de Cuenta, que recupera un token.
Login( Match( Account reference , { password: password } ))
Usamos este token en el cliente para hacerse pasar por la Cuenta. Dado que todas las Cuentas son miembros de la colección de Cuentas, este token cumple con el requisito de membresía logged_in_role
y se le otorga acceso para llamar a la create_fweet
UDF.
Para iniciar todo este proceso, tenemos dos roles muy importantes.
bootstrap_role
: sólo puede llamar a las UDFlogin
yregister
logged_in_role
: puede llamar a otras funciones comocreate_fweet
El token que recibió cuando ejecutó el script de configuración es esencialmente una clave creada con el archivo bootstrap_role
. Con ese token se crea un cliente en src/fauna/query-manager.js
el que sólo podrá registrarse o iniciar sesión. Una vez que iniciamos sesión, usamos el nuevo token devuelto Login()
para crear un nuevo cliente FaunaDB que ahora otorga acceso a otras funciones UDF como create_fweet
. Cerrar sesión significa que simplemente volvemos al token de arranque. Puede ver este proceso en src/fauna/query-manager.js
, junto con ejemplos de funciones más complejas en el src/fauna/setup/roles.js
archivo.
Cómo implementar la sesión en React
Anteriormente, en la sección "Creación del front-end", mencionamos el SessionProvider
componente. En React, los proveedores pertenecen a un contexto de React, que es un concepto para facilitar el intercambio de datos entre diferentes componentes. Esto es ideal para datos como la información del usuario que necesita en todas partes de su aplicación. Al insertar el SessionProvider
HTML desde el principio, nos aseguramos de que cada componente tuviera acceso a él. Ahora, lo único que tiene que hacer un componente para acceder a los detalles del usuario es importar el contexto y usar el gancho 'useContext' de React.
import SessionContext from '../context/session'import React, { useContext } from 'react'// In your componentconst sessionContext = useContext(SessionContext)const { user } = sessionContext.state
Pero ¿cómo termina el usuario en el contexto? Cuando incluimos SessionProvider, pasamos un valor que consta del estado actual y una función de envío.
const [state, dispatch] = React.useReducer(sessionReducer, { user: null })// ...SessionProvider value={{ state, dispatch }}
El estado es simplemente el estado actual y se llama a la función de envío para modificar el contexto. Esta función de envío es en realidad el núcleo del contexto, ya que crear un contexto solo implica llamar React.createContext()
a lo que le dará acceso a a Provider
y a Consumer
.
const SessionContext = React.createContext({})export const SessionProvider = SessionContext.Providerexport const SessionConsumer = SessionContext.Consumerexport default SessionContext
Podemos ver que el estado y el envío se extraen de algo que React llama reductor (usando React.useReducer
), así que escribamos un reductor.
export const sessionReducer = (state, action) = { switch (action.type) { case 'login': { return { user: action.data.user } } case 'register': { return { user: action.data.user } } case 'logout': { return { user: null } } default: { throw new Error(`Unhandled action type: ${action.type}`) } }}
Esta es la lógica que te permite cambiar el contexto. En esencia, recibe una acción y decide cómo modificar el contexto dado esa acción. En mi caso, la acción es simplemente un tipo con una cadena. Usamos este contexto para conservar la información del usuario, lo que significa que lo invocamos al iniciar sesión correctamente con:
sessionContext.dispatch({ type: 'login', data: e })
Agregar Cloudinary para medios
Cuando creamos un Fweet, todavía no tomamos en cuenta los activos. FaunaDB está destinado a almacenar datos de aplicaciones, no imágenes o datos de vídeo. Sin embargo, podemos almacenar fácilmente los medios en Cloudinary y simplemente mantener un enlace en FaunaDB. Lo siguiente inserta el script Cloudinary (en app.js
):
loadScript('https://widget.cloudinary.com/v2.0/global/all.js')
Luego creamos un widget de carga de Cloudinary (en src/components/uploader.js
):
window.cloudinary.createUploadWidget( { cloudName: process.env.REACT_APP_LOCAL___CLOUDINARY_CLOUDNAME, uploadPreset: process.env.REACT_APP_LOCAL___CLOUDINARY_TEMPLATE, }, (error, result) = { // ... })
Como se mencionó anteriormente, debe proporcionar un nombre de nube y una plantilla de Cloudinary en las variables de entorno ( .env.local
archivo) para usar esta función. Crear una cuenta de Cloudinary es gratis y una vez que tenga una cuenta, puede obtener el nombre de la nube del archivo dashboard
.
También tiene la opción de utilizar claves API para proteger las cargas. En este caso, cargamos directamente desde el front-end, por lo que la carga utiliza una plantilla pública. Para agregar una plantilla o modificarla para hacerla pública, haga clic en el ícono de ajustes en el menú superior, vaya a la pestaña Cargar y haga clic en Agregar configuración preestablecida de carga .
También puedes editar la plantilla ml_default y simplemente hacerla pública.
Ahora, simplemente llamamos widget.open()
cuando se hace clic en nuestro botón multimedia.
const handleUploadClick = () = { widget.open()} return ( div FontAwesomeIcon icon={faImage} onClick={handleUploadClick}/FontAwesomeIcon /div)
Esto nos proporciona un pequeño botón multimedia que abrirá el widget de carga de Cloudinary cuando se haga clic en él.
Cuando creamos el widget, también podemos proporcionar estilos y fuentes para darle la apariencia de nuestra propia aplicación como lo hicimos anteriormente (en src/components/uploader.js
):
const widget = window.cloudinary.createUploadWidget( { cloudName: process.env.REACT_APP_LOCAL___CLOUDINARY_CLOUDNAME, uploadPreset: process.env.REACT_APP_LOCAL___CLOUDINARY_TEMPLATE, styles: { palette: { window: '#E5E8EB', windowBorder: '#4A4A4A', tabIcon: '#000000', // ... }, fonts: {
Una vez que hemos subido medios a Cloudinary, recibimos una gran cantidad de información sobre los medios cargados, que luego agregamos a los datos cuando creamos un Fweet.
Luego podemos simplemente usar el almacenado id
(al que Cloudinary se refiere como publicId) con la biblioteca Cloudinary React (en src/components/asset.js
):
import { Image, Video, Transformation } from 'cloudinary-react'
Para mostrar la imagen en nuestro feed.
div className="fweet-asset" Image publicId={asset.id} cloudName={cloudName} fetchFormat="auto" quality="auto" secure="true" //div
Cuando utiliza la identificación, en lugar de la URL directa, Cloudinary realiza una amplia gama de optimizaciones para entregar los medios en el formato más óptimo posible. Por ejemplo, cuando agrega una imagen de video de la siguiente manera:
div className="fweet-asset" Video playsInline autoPlay loop={true} controls={true} cloudName={cloudName} publicId={publicId} Transformation fetchFormat="auto" crop="scale" / /Video/div
Cloudinary reducirá automáticamente la escala del vídeo a un ancho de 600 píxeles y lo entregará como WebM (VP9) a los navegadores Chrome (482 KB), MP4 (HEVC) a los navegadores Safari (520 KB) o MP4 (H.264). ) a navegadores que no admiten ninguno de los formatos (821 KB). Cloudinary realiza estas optimizaciones en el lado del servidor, lo que mejora significativamente el tiempo de carga de la página y la experiencia general del usuario.
Recuperando datos
Hemos mostrado cómo agregar datos. Ahora todavía necesitamos recuperar datos. Obtener los datos de nuestro feed de Fwitter tiene muchos desafíos. Necesitamos que:
- Recibe mensajes de las personas que sigues en un orden específico (teniendo en cuenta el tiempo y la popularidad)
- Consigue que el autor del fweet muestre su imagen de perfil y maneje
- Obtén las estadísticas para mostrar cuántos me gusta, refweets y comentarios tiene
- Obtenga los comentarios para enumerar los que están debajo del mensaje.
- Obtén información sobre si ya te gustó, retuiteaste o comentaste este fweet específico.
- Si es un refweet, obtén el fweet original.
Este tipo de consulta obtiene datos de muchas colecciones diferentes y requiere indexación/clasificación avanzada, pero comencemos de manera simple. ¿Cómo conseguimos los Fweets? Comenzamos obteniendo una referencia a la colección Fweets usando la Collection()
función.
Collection('fweets')
Y lo envolvemos en la Documents()
función para obtener todas las referencias de documentos de la colección.
Documents(Collection('fweets'))
Luego paginamos sobre estas referencias.
Paginate(Documents(Collection('fweets')))
Paginate()
requiere alguna explicación. Antes de llamar Paginate()
, tuvimos una consulta que devolvió un conjunto hipotético de datos. Paginate()
en realidad materializa esos datos en páginas de entidades que podemos leer. FaunaDB requiere que usemos esta Paginate()
función para protegernos de escribir consultas ineficientes que recuperen todos los documentos de una colección, porque en una base de datos construida a escala masiva, esa colección podría contener millones de documentos. Sin la protección de Paginate(), ¡eso podría resultar muy caro!
Guardemos esta consulta parcial en una variable JavaScript simple references
sobre la que podemos continuar desarrollando.
Deja un comentario