By Sails.js Tech Brasil

Passo a passo para um novo Protótipo Sails.js

Em termos gerais, Sails.js é um gerador de códigos JavaScript que entrega, em poucos minutos, um protótipo executável de uma Aplicação WEB do tipo backend robusta, segura e escalável. O resultado final é a capacidade de entregar páginas HTML5 + CSS + JavaScript dinâmicas ao Cliente Web.

Se esta é sua primeira vez aqui, recomendamos a leitura dos conteúdos abaixo antes de continuar:


Pré-requisitos

Primeira Etapa: Protótipo Raiz (5 min)
  1. Instalação do Sails.js
    npm i sails -g
  2. Criando o protótipo
    sails new primeiro-prototipo
    Quando perguntado, responda: 1. Web App
     Choose a template for your new Sails app:
     1. Web App  ·  Extensible project with auth, login, & password recovery
     2. Empty    ·  An empty Sails app, yours to configure
     (type "?" for help, or <CTRL+C> to cancel)
    ? 1
              
  3. Navegando para o diretório raiz do Protótipo
    cd primeiro-prototipo
  4. Executando o Primeiro Protótipo pela primeira vez
    sails lift você verá:
     info: Starting app...
    
     info: Initializing project hook... (`api/hooks/custom/`)
     info: Initializing `apianalytics` hook...  (requests to monitored routes will be logged!)
     info: ·• Auto-migrating...  (alter)
     info:    Hold tight, this could take a moment.
     info:  ✓ Auto-migration complete.
    
    debug: Running v0 bootstrap script...  (looks like this is the first time the bootstrap has run on this computer)
     info:
     info:                .-..-.
     info:
     info:    Sails              <|    .-..-.
     info:    v1.5.8              |\
     info:                       /|.\
     info:                      / || \
     info:                    ,'  |'  \
     info:                 .-'.-==|/_--'
     info:                 `--'-------'
     info:    __---___--___---___--___---___--___
     info:  ____---___--___---___--___---___--___-__
     info:
     info: Server lifted in `C:\Users\rogeriorfs\git\passo-a-passo`
     info: To shut down Sails, press <CTRL> + C at any time.
     info: Read more at https://sailsjs.com/support.
    
    debug: -------------------------------------------------------
    debug: :: Mon Jul 17 2023 08:15:43 GMT-0300 (Horário Padrão de Brasília)
    
    debug: Environment : development
    debug: Port        : 1337
    debug: Local       : http://localhost:1337
    debug: -------------------------------------------------------
              
    >
  5. Utilize o navegador (browser) de sua preferência e navegue para http://localhost:1337. O navegador deve apresentar algo do tipo:
    homepage.ejs example
  6. Se uma nova página se abriu, então continue na próxima etapa

Segunda Etapa: Criação de Nova Funcionalidade (10 min)
  1. Criando uma nova página. Digite na pasta raiz do seu projeto:
    sails generate page lista-tarefas
    Espera-se como resultado:
    Successfully generated:
     •- views\pages\lista-tarefas.ejs
     •- api\controllers\view-lista-tarefas.js
     •- assets\styles\pages\lista-tarefas.less
     •- assets\js\pages\lista-tarefas.page.js
    
    A few reminders:
     (1)  These files were generated assuming your Sails app is using
          Vue.js as its front-end framework.  (If you're unsure,
          head over to https://sailsjs.com/support)
    
     (2)  You'll need to manually add a route for this new page's
          action in your `config/routes.js` file; e.g.
              'GET /lista-tarefas': { action: 'view-lista-tarefas' },
    
     (3)  You'll need to manually import the new LESS stylesheet
          from your `assets/styles/importer.less` file; e.g.
              @import 'pages/lista-tarefas.less';
    
     (4)  Last but not least, since some of the above are backend changes,
          don't forget to re-lift the server before testing!
            
  2. Siga a recomendação e coloque no interior do arquivo config/routes.js a linha 'GET /lista-tarefas': { action: 'view-lista-tarefas' },
  3. Abra o arquivo views/pages/dashboard/welcome.ejs e adicione
    <a class="btn btn-info" href="/lista-tarefas">Minha Lista de Tarefas</a>
  4. Reinicie seu Protótipo, e faça login como admin@example.com senha abc123
    logine.ejs-example
  5. Você deverá visualizar algo do tipo:
    elcome.ejs-example
  6. Clique no botão [Minha Lista de Tarefas] e consulte sua página criada.
  7. Você deve ter visualizado no navegador o seguinte:
    list-tarefas.ejs-example-1
  8. Depois disso tente localizar sua saída padrão (console / TTY). O Sails deve ter enviado para ela o seguinte:
    <- GET /login                                 (6ms 200)
     |  view: pages/entrance/login
     °
    <- PUT /api/v1/entrance/login                 (226ms 200)
     |  The requesting user agent has been successfully logged in.
     |
     |  Under the covers, this stores the id of the logged-in user in the session as the `userId` key.  The next time this user agent sends a request, assuming it includes a cookie (like a web browser), Sails will automatically make this user id available as req.session.userId in the corresponding action.  (Also note that, thanks to the included "custom" hook, when a relevant request is received from a logged-in user, that user's entire record from the database will be fetched and exposed as `req.me`.)
     |
     °
    <- GET /                                      (3ms 302)
     |  redirect
     |  Requesting user is logged in, so redirect to the internal welcome page.
     °
    <- GET /welcome                               (6ms 200)
     |  Display the welcome page for authenticated users.
     |  view: pages/dashboard/welcome
     °
    <- POST /api/v1/observe-my-session            (2ms 200)
     |  The requesting socket is now subscribed to socket broadcasts about the logged-in user's session.
     °
    <- GET /lista-tarefas                         (6ms 200)
     |  view: pages/lista-tarefas
     °
            
  9. Se tudo ocorreu como esperado, então continue na próxima etapa

Terceira Etapa: Criando o modelo de dados (8 min)
  1. Na raiz de seu código, digite sails generate model tarefa
  2. Espera-se que o terminal responda com:
                info: Created a new model ("Tarefa")!
            
  3. Navegue para o diretório api/models e você deverá encontrar um arquivo api/models/Tarefa.js com o seguinte conteúdo:
    
    /**
     * Tarefa.js
     *
     * @description :: A model definition represents a database table/collection.
     * @docs        :: https://sailsjs.com/docs/concepts/models-and-orm/models
     */
    
    module.exports = {
    
      attributes: {
    
        //  ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦  ╦╔═╗╔═╗
        //  ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
        //  ╩  ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
    
    
        //  ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
        //  ║╣ ║║║╠╩╗║╣  ║║╚═╗
        //  ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
    
    
        //  ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
        //  ╠═╣╚═╗╚═╗║ ║║  ║╠═╣ ║ ║║ ║║║║╚═╗
        //  ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
    
      },
    
    };
                
  4. Edite o arquivo api/models/Tarefa.js e adicione os campos: prioridade, descricao e prazo, do seguinte modo:
    
    /**
     * Tarefa.js
     *
     * @description :: A model definition represents a database table/collection.
     * @docs        :: https://sailsjs.com/docs/concepts/models-and-orm/models
     */
    
    module.exports = {
    
      attributes: {
    
        //  ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦  ╦╔═╗╔═╗
        //  ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
        //  ╩  ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
        prioridade: {type: 'number', required: true, description: 'Registra a prioridade de atendimento'},
        descricao:  {type: 'string', required: true, description: 'Registra o que deve ser feito'},
        prazo:      {type: 'string', columnType: 'date', required: false, description: 'Data limite, se existir'},
    
        //  ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
        //  ║╣ ║║║╠╩╗║╣  ║║╚═╗
        //  ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
    
    
        //  ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
        //  ╠═╣╚═╗╚═╗║ ║║  ║╠═╣ ║ ║║ ║║║║╚═╗
        //  ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
    
      },
    
    };
            
  5. Reinicie seu protótipo com sails lift e, se nenhuma mensagem de erro aparecer, passe para a etapa seguinte.

Quarta Etapa: Adicionando dados para teste (3 min)
  1. Abra o arquivo config/bootstrap.js e localize a linha // By convention, this is a good place to set up fake data during development. (provavelmente a linha 61)
  2. Adicione logo abaixo desta linha o seguinte código JavaScript await Tarefa.create({prioridade:1,descricao:'Teste de tarefa', prazo: new Date()})
  3. Localize e apague os arquivos .tmp/localDiskDb/tarefa.db e .tmp/bootstrap-version.json
  4. Reinicie seu Protótipo com o comando sails lift
  5. Abra novamente o arquivos .tmp/localDiskDb/tarefa.db e verifique se seus dados criados acima já aparecem na coleção tarefa.db. Espera-se que o arquivo agora contenha algo do tipo:
    
    {"$$indexCreated":{"fieldName":"id","unique":true,"sparse":false}}
    {"prioridade":1,"descricao":"Teste de tarefa","prazo":"2023-07-12T14:27:20.992Z","createdAt":1689172040992,"updatedAt":1689172040992,"id":1,"_id":1}
    
                
  6. Se os dados já estão lá, então passe para a próxima etapa

Quinta Etapa: Visão do Usuário (Página) (10 min)
  1. Localize e faça a edição do arquivo api/controllers/view-lista-tarefas.js - criado na segunda etapa - para que tenha o seguinte código:
    
     module.exports = {
    
    
      friendlyName: 'View lista tarefas',
    
    
      description: 'Display "Lista tarefas" page.',
    
    
      exits: {
    
        success: {
          viewTemplatePath: 'pages/lista-tarefas'
        }
    
      },
    
    
      fn: async function () {
    
        let tarefas = await Tarefa.find({});
        return {tarefas};
    
      }
    
    
    };
    
                
  2. Agora localize e faça a edição do arquivo assets/js/pages/lista-tarefas.page.js para que ele obtenha da sessão HTTP os objetos disponibilizados pelo Controller, conforme abaixo:
    
    parasails.registerPage('lista-tarefas', {
      //  ╦╔╗╔╦╔╦╗╦╔═╗╦    ╔═╗╔╦╗╔═╗╔╦╗╔═╗
      //  ║║║║║ ║ ║╠═╣║    ╚═╗ ║ ╠═╣ ║ ║╣
      //  ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝  ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
      data: {
      },
    
      //  ╦  ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦  ╔═╗
      //  ║  ║╠╣ ║╣ ║  ╚╦╝║  ║  ║╣
      //  ╩═╝╩╚  ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
      beforeMount: function() {
        /* captura as variaveis / objetos da sessao   */
        /* e atribui a este objeto 'this'.            */
        /* Deste modo, o Vue2 pode acessar a lista    */
        /* de {tarefas} obtida no BD pelo Controller. */
        _.extend(this, window.SAILS_LOCALS);
    
      },
      mounted: async function() {
        //
      },
    
      //  ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
      //  ║║║║ ║ ║╣ ╠╦╝╠═╣║   ║ ║║ ║║║║╚═╗
      //  ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
      methods: {
        //…
      }
    });
            
  3. E finalmente adicione na Página Visão do Usuário os objetos trocados entre o backend e o frontend views/pages/lista-tarefas.ejs, conforme abaixo:
    
    <div class="container" id="lista-tarefas" v-cloak>
    
    	<p>Listagem montada no frontend pelo Vue 2 (a partir do window.SAILS_LOCALS)</p>
    	<ol>
    
    	  <li v-for='tarefa in tarefas'>{{tarefa.descricao}}</li>
    
    	</ol>
    
        <p>Listagem montada no backend pela renderização do template EJS (passagem de parametros pelo <em>Enginer</em> Sails</p>
    
        <ol>
            <% tarefas.forEach(tarefa=>{  %>
            <li><%=tarefa.descricao%></li>
            <% }) %>
        </ol>
    
    
    </div>
    <%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
    
            
  4. Reinicie seu protótipo com o comando sails lift, faça login e e navegue para sua Lista de Tarefas (conforme orientado acima). você deverá visualizar:
    lista-tarefas.ejs-example-2
  5. Se a descrição da tarefa teste aparecer na tela, então passe para a próxima etapa.

Sexta Etapa: Localizando e obtendo um dado para edição (10 min)
  1. Crie uma nova página destinada a edição dos dados com o comando sails generate page edit-tarefas
  2. Siga as instruções apresentadas no terminal (TTY) e adicione ao arquivo config/routes.js a seguinte linha
                     'GET /edit-tarefas': { action: 'view-edit-tarefas' },
                
  3. Modifique a linha acima para que ela seja capaz de reconhecer o ID do objeto desejado:
                     'GET /edit-tarefas/:id': { action: 'view-edit-tarefas' },
                
  4. Localize e modifique o arquivo api\controllers\view-edit-tarefas.js para que tenha o seguinte conteúdo:
    
      module.exports = {
    
    
      friendlyName: 'View edit tarefas',
    
    
      description: 'Display "Edit tarefas" page.',
    
      inputs: {
        id: {type: 'string', required: true}
      },
    
    
      exits: {
    
        success: {
          viewTemplatePath: 'pages/edit-tarefas'
        }
    
      },
    
    
      fn: async function ({id}) {
    
        let instance = await Tarefa.findOne({id: id});
        return {instance};
    
      }
    
    
    };
    
                
  5. Localize e edite o arquivo assets\js\pages\edit-tarefas.page.js para que tenha o seguinte conteúdo:
    
    parasails.registerPage('edit-tarefas', {
      //  ╦╔╗╔╦╔╦╗╦╔═╗╦    ╔═╗╔╦╗╔═╗╔╦╗╔═╗
      //  ║║║║║ ║ ║╠═╣║    ╚═╗ ║ ╠═╣ ║ ║╣
      //  ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝  ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
      data: {
        tarefa: {},
        prazo: new Date(),
      },
    
      //  ╦  ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦  ╔═╗
      //  ║  ║╠╣ ║╣ ║  ╚╦╝║  ║  ║╣
      //  ╩═╝╩╚  ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
      beforeMount: function() {
        /* captura as variaveis / objetos da sessao   */
        /* e atribui a este objeto 'this'.            */
        /* Deste modo, o Vue2 pode acessar a lista    */
        /* de {tarefas} obtida no BD pelo Controller, */
        /* mas neste caso, vamos explicitar uma outra */
        /* entidade tarefa simplesmente para          */
        /* faciltiar a compreensao do codigo.         */
        _.extend(this, window.SAILS_LOCALS);
        this.tarefa = this.instance;
        this.prazo = new Date(this.tarefa.prazo);
      },
      mounted: async function() {
        //…
      },
    
      //  ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
      //  ║║║║ ║ ║╣ ╠╦╝╠═╣║   ║ ║║ ║║║║╚═╗
      //  ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
      methods: {
        /* Este metodo nao funcionara enquanto nao     */
        /* for adicionada a entrada 'saveTarefa' no    */
        /* arquivo cloud.setup.js. Aprenda como fazer  */
        /* isso na próxima etapa                       */
        save: async function(tarefa) {
          let result = await Cloud.saveTarefas.with({tarefa});
          alert(JSON.stringify(result));
          window.location.reload()
        }
      }
    });
    
    
                
  6. Localize e edite o arquivo views/pages/edit-tarefas.ejs para que tenha o seguinte conteúdo:
    
    <div id="edit-tarefas" class="container" v-cloak="" >
    
        <div class="display-3">Manipulação dos dados com Vue</div>
    
        <input type="hidden" name="_csrf" id="_csrf" value="<%=_csrf%>">
    
        <label for="prioridade">Prioridade</label>
        <input type="number" class="form-control" id="prioridade" name="prioridade" v-model="tarefa.prioridade" required>
    
        <label for="descricao">Descrição</label>
        <input type="text" class="form-control" id="descricao" name="descricao" v-model="tarefa.descricao" required>
    
        <label for="prazo">Prazo</label>
        <input type="date" class="form-control" id="prazo" name="prazo" v-model="prazo">
    
        <button class="btn btn-primary mt-2" @click="save(tarefa)">Enviar</button>
    
        <div class="display-3">Visualização dos dados com EJS</div>
    
        <label for="pri">Prioridade</label>
        <span class="form-control" id="pri"><%=instance.prioridade%></span>
    
        <label for="des">Descricao</label>
        <span class="form-control" id="des"><%=instance.descricao%></span>
    
        <label for="praz">Prazo</label>
        <span class="form-control" id="praz" ><%=new Date(instance.prazo).toLocaleDateString()%></span>
    
    </div>
    <%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
    
    
            
  7. Localize e edite o arquivo /views/pages/lista-tarefas.ejs para que tenha o seguinte conteúdo:
    <div id="lista-tarefas" v-cloak>
    
    	<p>Listagem montada no frontend pelo Vue 2 (a partir do window.SAILS_LOCALS)</p>
    	<ol>
    
            <li v-for='tarefa in tarefas'><a v-bind:href="'/edit-tarefas/'+tarefa.id" >{{tarefa.descricao}}</a></li>
    
    	</ol>
    
        <p>Listagem montada no backend pela renderização do template EJS (passagem de parametros pelo <em>Enginer</em> Sails</p>
    
        <ol>
            <% tarefas.forEach(tarefa=>{  %>
            <li><a href="/edit-tarefas/<%=tarefa.id%>"><%=tarefa.descricao%></a></li>
            <% }) %>
        </ol>
    
    
    </div>
    <%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
    
                
  8. reinicie seu protótipo com o comando sails lift, faça login e navegue até sua listagem de tarefas. Você provavelmente visualizará algo do tipo:
    list-tarefas.ejs-example-3
  9. Clique em um dos links disponibilizados e consulte os dados da tarefa:
    edit-tarefas.ejs-example-3

Sétima Etapa: Persistindo os dados (10 min)
  1. No console, digite o comando sails generate action save-tarefas para criar uma requisição POST destinada a atualização dos dados. O resultado esperado deve ser algo do tipo:
    
    Successfully generated:
     •- api/controllers/save-tarefas.js
    
    A few reminders:
     (1)  For most projects, you'll need to manually configure an explicit route
          in your `config/routes.js` file; e.g.
              'POST /api/v1/save-tarefas': { action: 'save-tarefas' },
    
     (2)  If you are using the built-in JavaScript SDK ("Cloud") for AJAX requests
          from client-side code, then after configuring a new route, you'll want to
          regenerate the SDK setup file using:
              sails run rebuild-cloud-sdk
    
     (3)  This new action was generated in the "actions2" format.
            [?] https://sailsjs.com/docs/concepts/actions
    
     (4)  Last but not least, since adding an action or route is a backend change,
          don't forget to re-lift the server before testing!
        
  2. Execute a recomendação (1), mas antes de executar a (2), abra o arquivo api/controllers/save-tarefas.js e faça as seguintes modificações:
    
    module.exports = {
    
    
      friendlyName: 'Save tarefas',
    
    
      description: '',
    
    
      inputs: {
        tarefa: {type: 'ref', required: true}
      },
    
    
      exits: {
        success: {
          description: 'Salvo/Atualizado com sucesso'
        }
      },
    
    
      fn: async function ({tarefa}) {
    
        /* Este codigo recebe como parametro um objeto tarefa */
        /* e verifica se ele e novo ou nao para criar ou      */
        /* atualizar, conforme o caso, retornando o proprio   */
        /* objeto em seguida.                                 */
        if(tarefa.id&&tarefa.id!=='new'){
          return await Tarefa.updateOne({id: tarefa.id}, {
            prioridade: tarefa.prioridade,
            descricao: tarefa.descricao,
            prazo: tarefa.prazo
          });
        }else{
          return await Tarefa.create({
            prioridade: tarefa.prioridade,
            descricao: tarefa.descricao,
            prazo: tarefa.prazo
          }).fetch();
        }
    
    
      }
    
    
    };
    
        
  3. Agora execute a recomendação (2) digitando no console/terminal sails run rebuild-cloud-sdk. Espera-se algo do tipo:
     info: Initializing project hook... (`api/hooks/custom/`)
     info: Initializing `apianalytics` hook...  (requests to monitored routes will be logged!)
     info: ·• Auto-migrating...  (alter)
     info:    Hold tight, this could take a moment.
     info:  ✓ Auto-migration complete.
    
     info: --
     info: Successfully rebuilt Cloud SDK for use in the browser.
     info: (and CLOUD_SDK_METHODS.json for use in automated tests)
        
  4. Localize e confira no conteudo do arquivo assets/js/cloud.setup.js se uma nova entrada, como abaixo, foi adicionada ao arquivo:
            'saveTarefas': {'verb': 'POST', 'url': '/api/v1/save-tarefas', 'args': ['tarefa']}
        
  5. Se tudo correu bem até aqui, então você já pode reiniciar seu protótipo com o comando sails lift, fazer o login, navegar até a lista de tarefas, selecionar uma tarefa para edição, modificar os dados e clicar no botão [Enviar].
  6. Localize o arquivo .tmp/localDiskDb/tarefa.db (ou simplesmente volte para a 'listagem de tarefas') e verifique se os dados foram alterados.

Em resumo:

Em aproximadamente 45 minutos...

Observe que: este passo a passo é só um resumo!
Há MUITO mais facilidades e possibilidades de desenvolvimento com Sails.js, além do demostrado acima.

Para saber mais:
Início