Nuxt as fullstack server: frontend + backend API Server (Part 1)

Nuxt as fullstack server: frontend + backend API Server


Nuxt developers offer 3 methods of accessing the API:

  1. Built in Nuxt Server Connect and use serverMiddleware
  2. Integrated frameworks ( Express, Koa, Hapi , etc.)
  3. External server APIs

I will show with a simple example how to organize an API server using serverMiddleware on the same Nuxt instance that we have in frontend.

What are the benefits of this approach:

  • Quick start and minimization of the technology stack, only Nuxt is needed , which will be a fullstack server : render frontend pages and act as a backend API server
  • , 1 Node Nuxt, , (Nginx, php ..). - Nuxt - (MySQL, PostgreSQL, MongoDB ..)
  • , . , , .
  • Nuxt Connect, Node () 55 . , Nuxt' (Express, Koa, Hapi ..) .
  • Despite the minimalism, we have complete freedom to scale our application, it is easy to deploy several fullstack Nuxt servers that interact with one database, plus deploy nginx to balance the load between several fullstack Nuxt servers . Also, with minor modifications, each Nuxt instance can act as the only API server (only backend server), and only the frontend server (only frontend server).
  • And a huge bonus !!! When using one instance for the frontend and backend, you can significantly speed up server page rendering ( SSR ), which I will discuss in Part 2

So let's get started.

1: Nuxt as API Server


  1. /api/ , API
  2. API Server :

    /serverMiddleware/api-server.js
    export default async (req, res, next) => {
      let url = req._parsedUrl.pathname.replace(/^\/+|\/+$|\.+/g, "");
      url = url.split("/");
      let method = url.pop();
      let controller = url.slice(1).join("/");
      let api = require("../api/" + controller);
      let result = await api[method](req.params);
      res.end(JSON.stringify(result));
    };
    
  3. API Server Nuxt serverMiddleware

    /nuxt.config.js
      serverMiddleware: [
        { path: "/api", handler: require("body-parser").json() },
        {
          path: "/api",
          handler: (req, res, next) => {
            const url = require("url");
            req.query = url.parse(req.url, true).query;
            req.params = { ...req.query, ...req.body };
            next();
          }
        },
        { path: "/api", handler: "~/serverMiddleware/api-server.js" }
      ],
    

    http://<server_name>/api/controller_name/method_name /api/ controller_name.js, method_name
  4. users

    /api/users.js
    // we can get data from any DB
    async function getDataFromDB() {
      return (await require("axios").get(`https://reqres.in/api/users?page=1`))
        .data;
    }
    
    async function index() {
      let res = await getDataFromDB();
    
      // some business logic
      let ret = res.data.map(el => {
        return {
          id: el.id,
          full_name: el.first_name + " " + el.last_name,
          highlight: el.first_name.charAt(0) === "J"
        };
      });
      ret = ret.sort(() => 0.5 - Math.random());
    
      return ret;
    }
    
    export { index };
    

    ,

    3gzhl.sse.codesandbox.io/api/users/index
    [
      {
        "id": 3,
        "full_name": "Emma Wong",
        "highlight": false
      },
      {
        "id": 5,
        "full_name": "Charles Morris",
        "highlight": false
      },
      {
        "id": 1,
        "full_name": "George Bluth",
        "highlight": false
      },
      {
        "id": 2,
        "full_name": "Janet Weaver",
        "highlight": true
      },
      {
        "id": 4,
        "full_name": "Eve Holt",
        "highlight": false
      },
      {
        "id": 6,
        "full_name": "Tracey Ramos",
        "highlight": false
      }
    ]
    
  5. index.vue API

    /pages/index.vue
    <template>
      <div>
        <h1 class="title">NUXT as API Server</h1>
        <h2 class="subtitle">Users</h2>
        <button type="button" @click="$fetch">Refresh</button>
        <ol>
          <li
            v-for="user in users"
            :class="{'highlight': user.highlight}"
            :key="user.id"
          >{{ user.full_name }}</li>
        </ol>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          users: []
        };
      },
      async fetch() {
        try {
          this.users = await this.$axios.$get("/api/users/index");
        } catch (e) {
          console.error(e);
          throw e;
        }
      }
    };
    </script>
    
    <style scoped>
    .highlight {
      color: green;
      font-weight: bold;
    }
    </style>
    

    3gzhl.sse.codesandbox.io



    fetch , HTTP (https://3gzhl.sse.codesandbox.io/api/users/index), API json, .

    Refresh, http API .
  6. :

    API users (/api/users.js)

    ...
    async function show({ id }) {
      let res = await getDataFromDB();
    
      return res.data.filter(el => el.id == id)[0];
    }
    
    export { index, show };
    

    users

    /pages/index.vue
    <template>
      <div>
        <h1 class="title">NUXT as API Server</h1>
        <h2 class="subtitle">Users</h2>
        <button type="button" @click="$fetch">Refresh</button>
        <ol>
          <li
            v-for="user in users"
            :class="{'highlight': user.highlight}"
            :key="user.id"
            @click="getUserInfo(user.id)"
          >{{ user.full_name }}</li>
        </ol>
        <div v-if="userInfo">
          <hr>
          <h3>{{ userInfo.first_name }} {{ userInfo.last_name }}</h3>
          <div>Email: {{ userInfo.email }}</div>
          <img :src="userInfo.avatar" alt="avatar">
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          users: [],
          userInfo: null
        };
      },
      async fetch() {
        try {
          this.users = await this.$axios.$get("/api/users/index");
        } catch (e) {
          console.error(e);
          throw e;
        }
      },
      methods: {
        async getUserInfo(id) {
          try {
            this.userInfo = await this.$axios.$post("/api/users/show", {
              id: id
            });
          } catch (e) {
            console.error(e);
            throw e;
          }
        }
      }
    };
    </script>
    
    <style scoped>
    li {
      cursor: pointer;
    }
    .highlight {
      color: green;
      font-weight: bold;
    }
    </style>
    

    Users, HTTP 3gzhl.sse.codesandbox.io/api/users/show?id=4

    {"id":4,"email":"eve.holt@reqres.in","first_name":"Eve","last_name":"Holt","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"}
    


  7. .. API, . API

    /plugins/api-context.js
    export default (context, inject) => {
        inject('api', async (controller, method, params) => {
            try {
                return await context.$axios['$' + (params ? 'post' : 'get')]('/api/' + controller + '/' + method, params);
            } catch (e) {
                console.error(e);
                throw e;
            }
        })
    }
    



    nuxt.config.js
    export default {
      ...
      plugins: ["~/plugins/api-context.js"]
    }
    

    ( ) :

    /pages/index.vue
    ...
    <script>
    export default {
      ...
      async fetch() {
        this.users = await this.$api("users", "index");
      },
      methods: {
        async getUserInfo(id) {
          this.userInfo = await this.$api("users", "show", {id: id});
        }
      }
    };
    </script>
    


1


We got Nuxt with a full API server.

  • our server controllers are in the / api / folder
  • the logic for processing API requests by the server lies
    /serverMiddleware/api-server.js
  • we can easily expand the functionality of the server API by adding new middleware to / serverMiddleware / and connecting them to nuxt.config.js

→ The final version of this example can be viewed here
→ Sandbox here

All Articles