Como criar um aplicativo de bate-papo em vinte minutos

imagem



Meu pai gosta de me lembrar que, como engenheiro de computação na década de 1970, " ele era um programador antes de a programação estar na moda ". Ele até mostrou antigos scripts Fortran e COBOL algumas vezes. Depois de ler este código, posso dizer com segurança que a programação é definitivamente mais legal hoje .



Uma marca registrada das linguagens de programação e ambientes de desenvolvimento modernos é quanto menos código um desenvolvedor precisa escrever. Usando linguagens de alto nível junto com as muitas APIs disponíveis, pacotes de código aberto e serviços pagos, os aplicativos - mesmo aqueles com requisitos complexos - podem ser construídos com bastante rapidez.



Uma comparação para demonstrar a evolução do desenvolvimento de software é a construção. Era uma vez, a construção de qualquer casa começava com o corte de árvores em seu site. No entanto, materiais, ferramentas e métodos apareceram rapidamente para que a construção fosse concluída mais rapidamente, os objetos se tornassem mais fortes e os trabalhadores fossem libertados de algumas tarefas elementares.



Quantos arranha-céus seriam construídos se os construtores extraíssem seu próprio aço?



Os desenvolvedores de software, que continuam trabalhando até hoje, no início de suas carreiras "cortam suas próprias árvores". Ao mesmo tempo, inovações sem precedentes da última década levaram ao fato de que a indústria de software começou a se desenvolver da mesma forma que a construção.



Simplificando, os desenvolvedores modernos agora têm as ferramentas, técnicas e práticas recomendadas para concluir projetos mais rapidamente, obter aplicativos estáveis ​​e salvar os desenvolvedores de tarefas de baixo nível.



Como fazer um aplicativo de chat



Vamos criar rapidamente algo que costumava levar dias ou semanas. Faremos um aplicativo de sala de chat pública que usa WebSockets para mensagens em tempo real.



WebSockets são nativamente suportados por todos os navegadores modernos. No entanto, nosso objetivo é descobrir quais ferramentas podemos usar no trabalho, não reinventá-las . Com isso em mente, usaremos as seguintes tecnologias:



  • 8base  - API GraphQL gerenciada
  • VueJS  - framework JavaScript


O projeto inicial e o arquivo README completo podem ser encontrados neste repositório GitHub . Se você quiser apenas ver o aplicativo concluído, dê uma olhada na ramificação da sala de bate-papo pública.



Além disso, o vídeo abaixo (em inglês) explica cada etapa com mais detalhes.



Vamos começar.



Sete etapas para criar um aplicativo de chat:



1. Configuração do projeto



Clone o projeto inicial e vá para o diretório do chat em grupo. Você pode decidir por si mesmo se usará yarn ou npm para instalar dependências do projeto. Em qualquer caso, precisamos de todos os pacotes NPM especificados no arquivo package.json.



#  
git clone https://github.com/8base/Chat-application-using-GraphQL-Subscriptions-and-Vue.git group-chat
#   
cd group-chat
#  
yarn


Para interagir com a API GraphQL, precisamos configurar três variáveis ​​de ambiente. Crie um arquivo .env.local no diretório raiz com o seguinte comando e o aplicativo Vue, após a inicialização, definirá automaticamente as variáveis ​​de ambiente que adicionamos a este arquivo. Ambos os valores e não devem ser alterados. Você só precisa definir o valor . Se você tiver um espaço de trabalho 8base que deseja usar para criar um aplicativo de bate-papo usando nosso tutorial, atualize o arquivo .env.local com seu ID de espaço de trabalho. Caso contrário, obtenha o ID do espaço de trabalho seguindo as etapas 1 e 2 do 8base Quickstart .



echo 'VUE_APP_8BASE_WORKSPACE_ID=<YOUR_8BASE_WORKSPACE_ID>

VUE_APP_8BASE_API_ENDPOINT=https://api.8base.com

VUE_APP_8BASE_WS_ENDPOINT=wss://ws.8base.com' \

> .env.local




VUE_APP_8BASE_API_ENDPOINTVUE_APP_8BASE_WS_ENDPOINTVUE_APP_8BASE_WORKSPACE_ID







2. Importar esquema



Agora precisamos preparar o lado do servidor. Na raiz deste repositório, você deve encontrar o arquivo chat-schema.json. Para importá-lo para o espaço de trabalho, você só precisa instalar a linha de comando 8base e fazer o login, e então importar o arquivo de esquema.



#  8base CLI
yarn global add 8base-cli
#  CLI
8base login
#      
8base import -f chat-schema.json -w <YOUR_8BASE_WORKSPACE_ID>


3. Acesso API



A tarefa final do back-end é permitir o acesso público à API GraphQL.



No console 8base, vá para App Services > Roles > Guest. Atualize as permissões definidas para postagens e usuários para que sejam verificadas ou definidas como Todos os registros (conforme mostrado na captura de tela abaixo).



A função de convidado determina o que o usuário que faz uma solicitação de API não autenticada tem permissão para fazer.



imagem

Editor de funções no console 8base.



4. Escrever consultas GraphQL



Nesta etapa, vamos definir e escrever todas as consultas GraphQL que precisaremos para nosso componente de bate-papo. Isso nos ajudará a entender quais dados estaremos lendo, criando e ouvindo (via WebSockets) usando a API.



O código a seguir deve ser colocado em um arquivo src / utils / graphql.js. Leia os comentários acima de cada constante exportada para entender o que cada consulta faz.




/* gql      graphQL */
import gql from "graphql-tag";
/* 1.    -   10  */
export const InitialChatData = gql`
{
  usersList {
    items {
      id
      email
    }
  }
  messagesList(last: 10) {
    items {
      content
      createdAt
      author {
        id
        email
      }
    }
  }
}
`;
/* 2.          */
export const CreateUser = gql`
mutation($email: String!) {
  userCreate(data: { email: $email, roles: { connect: { name: "Guest" } } }) {
    id
  }
}
`;
/* 3.   */
export const DeleteUser = gql`
mutation($id: ID!) {
  userDelete(data: { id: $id, force: true }) {
    success
  }
}
`;
/* 4.        */
export const UsersSubscription = gql`
subscription {
  Users(filter: { mutation_in: [create, delete] }) {
    mutation
    node {
      id
      email
    }
  }
}
`;
/* 5.          */
export const CreateMessage = gql`
mutation($id: ID!, $content: String!) {
  messageCreate(
    data: { content: $content, author: { connect: { id: $id } } }
  ) {
    id
  }
}
`;
/* 6.     . */
export const MessagesSubscription = gql`
subscription {
  Messages(filter: { mutation_in: create }) {
    node {
      content
      createdAt
      author {
        id
        email
      }
    }
  }
}
`;




5. Configurando o cliente Apollo para assinaturas



Com nossas consultas GraphQL escritas, é hora de configurar nossos módulos de API.



Primeiro, vamos lidar com o cliente API ApolloClientcom seus padrões obrigatórios. Para createHttpLinknós, fornecemos nosso terminal de espaço de trabalho totalmente formado. Este código está em src/utils/api.js.



import { ApolloClient } from "apollo-boost";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
const { VUE_APP_8BASE_API_ENDPOINT, VUE_APP_8BASE_WORKSPACE_ID } = process.env;

export default new ApolloClient({
link: createHttpLink({
  uri: `${VUE_APP_8BASE_API_ENDPOINT}/${VUE_APP_8BASE_WORKSPACE_ID}`,
}),
cache: new InMemoryCache(),
});

// Note:     ,    // ApolloClient,    .


Em seguida, lidaremos com o cliente de assinatura usando subscriptions-transport-wse isomorphic-ws. Este código é um pouco mais longo que o anterior, por isso vale a pena ler os comentários no código.



Inicializamos SubscriptionClientusando nosso endpoint WebSockets e workspaceIdem parâmetros connectionParams. Em seguida, usamos este subscriptionClientem dois métodos definidos na exportação padrão: subscribe()e close().



subscribenos permite criar novas assinaturas com dados e callbacks de erro. O método de fechamento é o que podemos usar para fechar a conexão quando sairmos do chat.



import WebSocket from "isomorphic-ws";
import { SubscriptionClient } from "subscriptions-transport-ws";
const { VUE_APP_8BASE_WS_ENDPOINT, VUE_APP_8BASE_WORKSPACE_ID } = process.env;

/**
*   ,  
*     .
*/

const subscriptionClient = new SubscriptionClient(
VUE_APP_8BASE_WS_ENDPOINT,
{
  reconnect: true,
  connectionParams: {
    /**
      * Workspace ID    ,  
*  Websocket  
*    
      */
    workspaceId: VUE_APP_8BASE_WORKSPACE_ID,
  },
},
/**
  *    WebSocket,   W3C. * ,        *WebSocket (,   NodeJS)
  */
WebSocket
);
export default {
/**
  *   ,      *'data’  'error’
  */
subscribe: (query, options) => {
  const { variables, data, error } = options;
  /**
    *     .
    */
  const result = subscriptionClient.request({
    query,
    variables,
  });
  /**
    *       * ,     , 
* subscriptionClient
    */
  const { unsubscribe } = result.subscribe({
    /**
      *       
* ,  .
      */
    next(result) {
      if (typeof data === "function") {
        data(result);
      }
    },
    /**
      *          ,  .
      */
    error(e) {
      if (typeof error === "function") {
        error(e);
      }
    },
  });
  return unsubscribe;
},
/**
  *  subscriptionClient .
  */
close: () => {
  subscriptionClient.close();
},
};
// .     SubscriptionClient   , 
// ,    .


6. Escrevendo um componente Vue



Agora temos tudo o que precisamos para criar um chat público. Resta apenas um componente para escrever GroupChat.vue.



Carregue o componente usando fio de saque e vamos continuar.



Nota importante: cada um tem sua própria ideia de beleza, então eu apenas criei os estilos mínimos necessários para o componente ser funcional.



Script de componente



Primeiro, precisamos importar nossos módulos, estilos simples e consultas GraphQL. Tudo isso está no nosso src / utils.

Declare as seguintes importações em GroupChat.vue.



/* API  */
import Api from "./utils/api";
import Wss from "./utils/wss";

/* graphQL  */
import {
InitialChatData,
CreateUser,
DeleteUser,
UsersSubscription,
CreateMessage,
MessagesSubscription,
} from "./utils/graphql";
/*  */
import "../assets/styles.css";


Dados de componentes



Podemos definir quais propriedades de dados queremos usar na função de dados de nosso componente. Tudo o que precisamos é uma maneira de armazenar usuários de bate-papo, mensagens, o nome do usuário "atual" e qualquer mensagem que ainda não tenha sido enviada. Essas propriedades podem ser adicionadas da seguinte forma:



/* imports ... */

export default {
name: "GroupChat",
data: () => ({
  messages: [],
  newMessage: "",
  me: { email: "" },
  users: [],
}),
};


Ganchos de ciclo de vida



Nossos ganchos de ciclo de vida funcionam em diferentes pontos da vida de um componente Vue. Por exemplo, quando é montado ou atualizado. Neste caso, estamos interessados ​​apenas na criação e beforeDestroycomponente. Nesses casos, queremos abrir as inscrições de bate-papo ou fechá-las.



/* ... */

export default {
/*   ... */

/**
  *   ,    .
  */
created() {
  /**
    *   ,       
    */
  Wss.subscribe(UsersSubscription, {
    data: this.handleUser,
  });
  /**
    *   ,     
    */
  Wss.subscribe(MessagesSubscription, {
    data: this.addMessage,
  });
  /**
    *     (   10 )
    */
  Api.query({
    query: InitialChatData,
  }).then(({ data }) => {
    this.users = data.usersList.items;
    this.messages = data.messagesList.items;
  });
  /**
    *     ,   
    */
  window.onbeforeunload = this.closeChat;
},
/**
  *   ,    .
  */
beforeDestroy() {
  this.closeChat();
},
};


Métodos de componente



Precisamos adicionar alguns métodos para processar cada resposta de chamada / API ( createMessage, addMessage, closeChat, etc.). Todos eles serão armazenados no objeto de método de nosso componente.

É necessário

observar uma coisa: a maioria das mutações não espera e não manipula as respostas. Isso ocorre porque temos assinaturas que rastreiam essas mutações. Após um lançamento bem-sucedido, os dados do evento são processados ​​pela assinatura.

A maioria

desses métodos fala por si. De qualquer forma, leia os comentários no código a seguir.



/*  ... */

export default {
/*   ... */
methods: {
  /**
    *   ,     .
    */
  createUser() {
    Api.mutate({
      mutation: CreateUser,
      variables: {
        email: this.me.email,
      },
    });
  },
  /**
    *     ID.
    */
  deleteUser() {
    Api.mutate({
      mutation: DeleteUser,
      variables: { id: this.me.id },
    });
  },
  /**
    *        ,   
*           
* .
*
*    ,      ,  
*   ,   .
    */
  handleUser({
    data: {
      Users: { mutation, node },
    },
  }) {
    ({
      create: this.addUser,
      delete: this.removeUser,
    }[mutation](node));
  },
  /**
    *      users,  , *     .
    */
  addUser(user) {
    if (this.me.email === user.email) {
      this.me = user;
    }
    this.users.push(user);
  },
  /**
    *     users  ID.
    */
  removeUser(user) {
    this.users = this.users.filter(
      (p) => p.id != user.id
    );
  },
  /*    */
  createMessage() {
    Api.mutate({
      mutation: CreateMessage,
      variables: {
        id: this.me.id,
        content: this.newMessage,
      },
    }).then(() => (this.newMessage = ""));
  },
  /**
    *        .  * ,    ,       *.
    */
  addMessage({
    data: {
      Messages: { node },
    },
  }) {
    this.messages.push(node);
  },
  /**
    *        .          beforeDestroy     .
    */
  closeChat () {
    /*     */
    Wss.close()
    /*   */
    this.deleteUser();
    /*     */
    this.me = { me: { email: '' } }
  }
},
/*  ... */
}


Modelo de componente



Por último, mas não menos importante, temos um componente GroupChat.vue.

Existem

milhares de excelentes tutoriais sobre como criar belas interfaces de usuário. Este não é um deles.

O

padrão a seguir Torná-lo bonito ou não é com você. Dito isso, vamos examinar rapidamente a marcação de chave que implementamos aqui.

Como

sempre, leia os comentários embutidos no código.



<template>
<div id="app">
  <!--
           ,     .      ..
    -->
  <div v-if="me.id" class="chat">
    <div class="header">
      <!--
           ,      ,  ,     ,   .
        -->
      {{ users.length }} Online Users
      <!--
           ,   closeChat..
        -->
      <button @click="closeChat">Leave Chat</button>
    </div>
    <!--
     ,      ,      div.  ,         ,     me.
      -->
    <div
      :key="index"
      v-for="(msg, index) in messages"
      :class="['msg', { me: msg.participant.id === me.id }]"
    >
      <p>{{ msg.content }}</p>
      <small
        ><strong>{{ msg.participant.email }}</strong> {{ msg.createdAt
        }}</small
      >
    </div>
    <!--
      newMessage.
      -->
    <div class="input">
      <input
        type="text"
        placeholder="Say something..."
        v-model="newMessage"
      />
      <!--
           ,    createMessage.
        -->
      <button @click="createMessage">Send</button>
    </div>
  </div>
  <!--
          .     ,   createUser.
    -->
  <div v-else class="signup">
    <label for="email">Sign up to chat!</label>
    <br />
    <input
      type="text"
      v-model="me.email"
      placeholder="What's your email?"
      @blur="createUser"
      required
    />
  </div>
</div>
</template>


E agora o chat público está construído. Se você abri-lo em sua rede local, poderá começar a enviar e receber mensagens. Porém, para provar que se trata de um verdadeiro chat em grupo, abra várias janelas e observe o andamento da conversa.



7. Conclusão e teste



Neste tutorial, exploramos como o uso de ferramentas de desenvolvimento modernas nos permite criar aplicativos do mundo real em minutos.



Espero que você também tenha aprendido como inicializar ApolloCliente SubscriptionClientexecutar consultas GraphQL, mutações e assinaturas de forma eficiente em um espaço de trabalho 8base, bem como um pouco sobre VueJS.



Esteja você trabalhando em um jogo móvel, mensageiros, aplicativos de notificação ou outros projetos que requerem dados em tempo real, as assinaturas são uma ótima ferramenta. E agora apenas começamos a considerá-los.



Crie um aplicativo de bate-papo com 8base



8base é um backend sem servidor pronto para uso, como um serviço criado por desenvolvedores para desenvolvedores. A plataforma 8base permite que os desenvolvedores criem aplicativos em nuvem impressionantes usando JavaScript e GraphQL. Saiba mais sobre a plataforma 8base aqui .



All Articles