part-11.jpg

Sails.js & Vue.js - Part 1

🛈

Lord High Admiral. We will begin the refit. Before we outfit the fleet with the latest enchantments, a true commander must understand the traditional craft. This first step will ground us in the fundamentals of how a modern front-end communicates with our Sails vessel using the ship’s existing systems.


Part 1: The Traditional Refit (Vue & The Asset Pipeline)

  • Current Rank: Lord High Admiral
  • Objective: To manually integrate a Vue.js component into a classic Sails EJS view, using the built-in asset pipeline and standard fetch requests to create a reactive island on a traditional web page.

Chief Architect’s Briefing (Background and Theory)

Our ships are currently built with EJS templates rendered on the server. When you visit /logbook, Sails compiles the HTML and sends a complete page to your browser. Our goal is to enhance these pages, not replace them entirely… yet.

We will treat Vue.js as a specialist crew member that we bring aboard to manage one specific, complex part of the deck. To do this, we will leverage Sails’ built-in Asset Pipeline.

The assets/ directory in your Sails project is a special place. Sails automatically processes the files within it. Any JavaScript file you place in assets/js/ will be automatically included as a <script> tag in your master layout file (views/layouts/layout.ejs). This is the system Sails has always used for front-end JavaScript.

Our strategy is as follows:

  1. We will add the Vue.js library to our project via a CDN link in our layout file.
  2. We will create a placeholder <div> in one of our EJS views. This will be the “station” where our Vue crew member works.
  3. We will write our Vue application in a separate JavaScript file within assets/js/.
  4. When the page loads, this script will find the placeholder <div> and “mount” our Vue application onto it, bringing that section of the page to life.
  5. Once alive, our Vue component will make a standard network request (fetch) to a Sails API endpoint to get its data, just like any third-party client would.

This approach creates a “reactive island” on an otherwise static page. It is powerful for enhancing specific components but, as we will see, it reveals why a more integrated solution like Inertia.js is so desirable for full applications.


Key Concepts Checklist

  • Asset Pipeline: Sails’ system in assets/ for managing and serving front-end JavaScript and CSS.
  • layout.ejs: The master view template where all scripts are loaded.
  • Vue CDN: Including the Vue library directly from a Content Delivery Network for simplicity.
  • Mounting an App: The core Vue concept of attaching a live application to a DOM element, e.g., createApp().mount('#app').
  • AJAX / Fetch: Standard browser APIs used by the front-end (Vue) to request JSON data non-blockingly from the back-end (Sails).

Mission Log: Quest - “The Live Cargo Manifest”

We will create a new, simple application. The goal is to display a ship’s cargo manifest. The manifest list itself will be rendered and managed by Vue, fetching its data from a Sails API endpoint.

  • Task 1: Lay the New Keel

    1. Create a fresh Sails project for this mission.

      sails new traditional-refit
      cd traditional-refit
      
  • Task 2: Bring the Specialist Aboard (Add Vue.js)

    1. Open the master layout file: views/layouts/layout.ejs.
    2. Just before the closing </body> tag, add the Vue.js script from its official CDN. Below that, we’ll link to our own future app script.

      <!-- ... existing scripts ... -->
      
      <!--Add Vue.js from CDN-->
      <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
      
      <!--Add our custom Vue application script-->
      <script src="/js/manifest-app.js"></script>
      
      </body>
      </html>
      
  • Task 3: Prepare the Workstation (The Mount Point)

    1. Open the default homepage view: views/pages/homepage.ejs.
    2. Replace its content with a simple structure, including the <div id="manifest-app"> where our Vue app will live.

      <div id="manifest-app">
        <h1>Live Cargo Manifest</h1>
        <div v-if="loading">Loading manifest from the ship's hold...</div>
        <ul v-else>
          <li v-for="item in cargo" :key="item.id">
            {{ item.name }} - Quantity: {{ item.quantity }}
          </li>
        </ul>
      </div>
      

      Notice the Vue-specific syntax like v-if and v-for. This will be inert HTML until Vue takes control of it.

  • Task 4: Create the API Endpoint

    1. Generate a new controller to serve our manifest data.

      sails generate controller ManifestController
      
    2. Open api/controllers/ManifestController.js and create an action to return JSON data.

      module.exports = {
        get: function(req, res) {
          const cargoData = [
            { id: 1, name: 'Barrels of Rum', quantity: 50 },
            { id: 2, name: 'Crates of Cannonballs', quantity: 100 },
            { id: 3, name: 'Secret Treasure Maps', quantity: 5 }
          ];
          return res.json(cargoData);
        }
      };
      
    3. Create a route for this endpoint in config/routes.js:

      'GET /api/v1/manifest': 'ManifestController.get',
      
  • Task 5: Write the Vue App Logic

    1. Create a new file: assets/js/manifest-app.js.
    2. Add the Vue code that will mount to our div, fetch data from the API, and manage the state.

      const { createApp } = Vue;
      
      createApp({
        data() {
          return {
            cargo: [],
            loading: true
          }
        },
        async mounted() {
          console.log('Vue app has mounted, fetching manifest...');
          const res = await fetch('/api/v1/manifest');
          this.cargo = await res.json();
          this.loading = false;
        }
      }).mount('#manifest-app');
      
  • Task 6: Hoist the Sails

    1. Start your server: sails lift.
    2. Open your browser to http://localhost:1337.
    3. You will briefly see “Loading manifest…” and then, almost instantly, the list of cargo items will appear. This list was not rendered by EJS; it was rendered on the client-side by Vue after it fetched data from your Sails API.

Mission Debrief (Review & Outcomes)

Excellent work, Admiral. You have successfully refitted a traditional vessel with a modern, reactive component. You now understand the fundamental mechanics: a server-rendered page loads, a JavaScript library is included, it mounts to a DOM element, and it fetches data from an API to manage its own small piece of the world.

However, consider the complexities. We had to manually create API endpoints for our front-end. We have Vue syntax sitting inside an EJS file. We wrote a manual fetch call. If we wanted to add a form to add new cargo, we’d have to write more manual fetch calls with POST requests and handle the response.

This approach works, but it feels… disconnected. It’s like our specialist Vue crew has to shout across the deck to the Sails crew to get anything done. What if they could communicate telepathically?

This is the very problem that Inertia.js was born to solve.


Rewards

  • +400 Doubloons
  • Achievement Unlocked: You have successfully bridged the gap between a classic back-end and a modern front-end framework.
    • Badge Earned: The Traditional Refitter 🛠️