<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Nuxt.js - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Nuxt.js - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 03 Jun 2026 17:24:19 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/nuxtjs/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Implement RBAC in a Community Dashboard with Nuxt ]]>
                </title>
                <description>
                    <![CDATA[ Role Based Access Control (RBAC) is a useful authorization model for users with different access levels, such as those in a community dashboard. In this article, you’ll learn how to integrate this type of authorization with Permit.io in Nuxt. Table o... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rbac-community-dashboard-with-nuxt/</link>
                <guid isPermaLink="false">6740bf4cd3bb67c73837c3ef</guid>
                
                    <category>
                        <![CDATA[ authorization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Fri, 22 Nov 2024 17:28:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732043730534/d88680d7-2590-4541-9120-40a43c3724ef.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Role Based Access Control (RBAC) is a useful authorization model for users with different access levels, such as those in a community dashboard.</p>
<p>In this article, you’ll learn how to integrate this type of authorization with Permit.io in Nuxt.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-table-of-contents">Table of Contents</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-difference-between-authentication-and-authorization">What is the Difference Between Authentication and Authorization?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-role-based-access-control-rbac">What is Role Based Access Control (RBAC)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-the-benefits-of-using-authorization-as-a-service">What are the Benefits of Using Authorization As A Service?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-well-be-building">What We’ll Be Building</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-plan-rbac-with-permitio">How to Plan RBAC with</a> <a target="_blank" href="http://Permit.io">Permit.io</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-setup-permitio-in-nuxt">How to Setup</a> <a target="_blank" href="http://Permit.io">Permit.io</a> <a class="post-section-overview" href="#heading-how-to-setup-permitio-in-nuxt">in Nuxt</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-control-api-access-with-nuxt-middleware">How to Control API Access with Nuxt Middleware</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-rbac-in-the-community-dashboard">How to Test RBAC in the Community Dashboard</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-difference-between-authentication-and-authorization">What is the Difference Between Authentication and Authorization?</h2>
<p>When building applications, we often carry out authentication together with authorization. However, these two concepts are essentially different.</p>
<p>Authentication is verifying who a user is. During the authentication process, the user usually needs to log in with some identifier like email, phone, username, <em>with</em> Google, <em>with</em> Microsoft, and so on.</p>
<p>Authorization specifies the resources an authenticated user can view and what they can do in the application. Authorization tells a user's access rights after that user has been successfully authenticated.</p>
<p>For example, authentication is a user logging in with email and password or verifying their phone number with SMS. On the other hand, authorization is a writer creating and editing posts while only admins can approve and publish those posts.</p>
<p>The primary purpose of authentication is to establish a user's identity before granting them access to the system. The main goal of authorization is to control user actions and protect sensitive data or resources.</p>
<h2 id="heading-what-is-role-based-access-control-rbac">What is Role Based Access Control (RBAC)?</h2>
<p>Role-Based Access Control (RBAC) is an authorization model that you can use to manage and restrict access to system resources. It is based on the responsibilities, duties, or roles of users.</p>
<p>In RBAC, roles represent predefined sets of permissions that tell which actions a user can execute in an application. These roles are then assigned to users based on their job functions or responsibilities.</p>
<p>In common systems, anyone can assign permissions to individual users. In RBAC, we group permissions into roles. In turn, we assign these roles to users. For example, in a community dashboard, users might have roles like “Admin”, “Mentor”, or “Member”.</p>
<p>Aside from Role-Based Access Control, other popular authorization models exist such as Attribute-Based (ABAC) and Relationship-based (ReBAC) access controls. Attribute-Based Access Control uses a wide range of attributes and fits constantly changing systems. You could also combine it with Role-Based Access Control. For more info, see the <a target="_blank" href="https://www.permit.io/blog/rbac-vs-abac">Permit.io article on RBAC vs. ABAC</a>.</p>
<h2 id="heading-what-are-the-benefits-of-using-authorization-as-a-service">What are the Benefits of Using Authorization As A Service?</h2>
<p>You can build authorization models yourself in your application. But it may be time-consuming and cost-ineffective in the long run.</p>
<p>Using an external provider for authorization allows you to focus on the business logic in your applications. The benefits of outsourcing authorization are similar to using a third party like Auth0 or Firebase for Authentication.</p>
<p><a target="_blank" href="https://www.permit.io/blog/authorization-as-a-service">Authorization as a Service</a> provides a solution for managing user access and permissions in applications. When you use such an authorization solution, you enjoy enhanced security, granular access policies’ control, auto-scaling policies, reduced maintenance burden, faster upgrades, robust logging, and so on.</p>
<p>Permit.io is free to use for <a target="_blank" href="https://docs.permit.io/manage-your-account/workspace-usage/#maus--tenants-usage">up to a 1000 Monthly Active Users</a>, and has a UI and API for RBAC, ABAC, and ReBAC.</p>
<p>To Get Started with Permit.io:</p>
<ul>
<li><p>Go to <a target="_blank" href="https://app.permit.io">app.permit.io</a></p>
</li>
<li><p>Create an account</p>
</li>
<li><p>Create a workspace (in your account)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037452876/4604ad72-c5cc-4e07-8f04-70ff4f3dbb8c.gif" alt="Demo of Getting Started with Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<h2 id="heading-what-well-be-building">What We’ll Be Building</h2>
<p>A community dashboard connects members within a community or forum. It is a platform where they can interact and access resources.</p>
<p>For demo purposes of RBAC, we’ll build a simple community dashboard that will include 3 types of content (entities): posts, materials, and announcements<em>.</em></p>
<p>The code we will be using is at <a target="_blank" href="https://github.com/obumnwabude/rbac-community-dashboard">https://github.com/obumnwabude/rbac-community-dashboard</a>. It consists of a Nuxt repo with its server configured for API calls and its front end built with Vue. Clone it with git and explore the code in an editor/IDE.</p>
<p>In the server, we will expose <em>GET, POST,</em> and <em>DELETE</em> endpoints for each entity (posts, materials, and announcements). In Nuxt, you can use the HTTP verb in the file name for the endpoint handler. So we can have <strong>posts.get.ts*</strong>,<em> <strong>posts.delete.ts</strong></em>,<em> <strong>materials.post.ts</strong></em>,* and so on, with each file containing the respective handler for the involved API endpoint.</p>
<p>In addition, the server files store and retrieve entities from JSON files. You should have a robust database setup in your product. For this project, we’ll use local JSON files to build a minimum reproducible example with focus on roles and authorization.</p>
<p>In the front end, we have four pages: three for the entities and a settings page. There is also a simple navigation: a bottom bar on smaller screens and a sidebar on wider ones. Each entity page shows a list of its items and a small form to create new ones.</p>
<p>Furthermore, the demo code uses <a target="_blank" href="https://tailwindcss.com/">tailwindcss</a> to style everything quickly. The settings page contains hardcoded user examples and roles for the demo. When testing, toggle the current user and see the roles in action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732031161755/36a4981c-bc1c-4156-afb8-05fab7d45ee7.gif" alt="Demo of Exploring the Community Dashboard" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>This article focuses on the parts of the code that deal with authorization. For the backend and the UI specifics of the community dashboard, we will only overview them. After that, we will deep dive into RBAC touchpoints.</p>
<h2 id="heading-how-to-plan-rbac-with-permitio">How to Plan RBAC with Permit.io</h2>
<p>Overall, planning authorization means mapping out “who” can carry out an action on “what<em>”</em>. In RBAC, we define roles and then assign them to users. The roles and users combo is the “who” part of the authorization.</p>
<p>The “what” side refers to the entities or resources that your application provides or manages. For this article’s example, we’ve chosen our resources as Posts, Materials, and Announcements.</p>
<p>Actions are user activity. The most common actions are “create<em>”, “</em>read<em>”, “</em>update<em>”,</em> and, “delete<em>”</em>. Per resource in your application, you can use these four actions, add more, or omit some. In this article, our resources will each have all four actions.</p>
<p>When planning authorization, define resources alongside the <em>“</em>actions” users can execute on each resource. After that, define roles. For each role, specify what actions a user holding that role can carry out on each resource. The mapping of resources, actions, and roles allows you to define the authorization policies of your application.</p>
<p>Permit.io makes it easy to edit policies. In Permit.io, you have an intuitive dashboard where you can create resources and their actions, create roles, and merge both with policy tables.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037510499/2c0acabb-e0b4-4002-8b7b-e2fd4dbb4777.gif" alt="Demo of Creating Resources and Actions in Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>For our Community Dashboard example, we will create three roles with incremental access: member, mentor, and admin. For each role, we’ll allow read access to all resources. However, each role has different management access levels to the resources as follows:</p>
<ul>
<li><p><strong>Members</strong> can view all entities but can only create or delete posts.</p>
</li>
<li><p><strong>Mentors</strong> can view all entities and can create or delete posts and materials.</p>
</li>
<li><p><strong>Admins</strong> can create, view, and delete all entities.</p>
</li>
</ul>
<p>Assigning roles to actions in resources is the same as editing policies.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037619254/e8900675-18c0-429c-9840-3ba8c1c38a6c.gif" alt="Demo of Creating Roles and Editing Policies in Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<h2 id="heading-how-to-setup-permitio-in-nuxt">How to Setup Permit.io in Nuxt</h2>
<p>For our demo project, you need to run <code>npm install</code>, create a <code>.env</code> file, and export your Permit token.</p>
<p>However, if you are building a new project, to setup Permit in Nuxt, first install it with npm.</p>
<pre><code class="lang-bash">npm install permitio
</code></pre>
<p>After that, create a <code>.env</code> file and add your PERMIT_TOKEN. Get the token from the dashboard.</p>
<pre><code class="lang-bash">PERMIT_TOKEN=permit_key_XXXXXXXXXXXXXXXXXXXXX
</code></pre>
<p>To make this token available to the Nuxt <code>runtimeConfig</code>, add it to the <code>nuxt.config.ts</code> file. Also, add <code>permitio</code> (alongside other dependencies) in the <code>transpile</code> array of the build property of the Nuxt config file.</p>
<p>This addition is to account for Nuxt’s specific optimizations. Your <code>nuxt.config.ts</code> file should look like the following:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// https://nuxt.com/docs/api/configuration/nuxt-config</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineNuxtConfig({
  <span class="hljs-comment">// ... other properties</span>

  build: {
    transpile: [<span class="hljs-string">'axios'</span>, <span class="hljs-string">'permitio'</span>, <span class="hljs-string">'pino'</span>]
  },
  runtimeConfig: {
    permitToken: process.env.PERMIT_TOKEN
  }
});
</code></pre>
<p>After these, Permit.io should be available to your server code in Nuxt. You can now use in it middleware code to check for permissions.</p>
<h2 id="heading-how-to-control-api-access-with-nuxt-middleware">How to Control API Access with Nuxt Middleware</h2>
<p>In simple terms, middleware is code that runs before a target handler. In Nuxt, you can add middleware for API endpoints by creating necessary files in a <code>middleware</code> directory contained in the top-level <code>server</code> directory.</p>
<p>Since we are dealing with permissions, we will name our middleware file <code>permissions.ts</code>. Here, you’ll check if a user is permitted to take an action on a given resource.</p>
<p>Permit.io makes this easy with a simple <code>.check</code> method that returns a Boolean indicating if the user is permitted.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">await</span> permit.check(user, action, resource);
</code></pre>
<p>For this simple example community dashboard, our middleware code will first try to determine the user and action from the request properties. The example code achieves that in crude ways. They are enough to explain the concept and you should use more robust industry-standard methods for this.</p>
<p>After that, in the example code below, we construct the Permit object using our permit token and the default public PDP (Policy-Decision-Point microservice)’s endpoint. The <a target="_blank" href="https://github.com/permitio/PDP">Permit PDP is open-source.</a> If you wish, you can set up your local/personal PDP by following the steps <a target="_blank" href="https://docs.permit.io/how-to/deploy/deploy-to-production">here</a>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Permit } <span class="hljs-keyword">from</span> <span class="hljs-string">'permitio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineEventHandler(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-comment">// Only check permissions if the request is a POST or DELETE request</span>
  <span class="hljs-keyword">const</span> { method, path } = event;
  <span class="hljs-keyword">if</span> (method !== <span class="hljs-string">'POST'</span> &amp;&amp; method !== <span class="hljs-string">'DELETE'</span>) <span class="hljs-keyword">return</span>;

  <span class="hljs-comment">// Ensure authorization header is present</span>
  <span class="hljs-keyword">let</span> authorization = event.node.req.headers[<span class="hljs-string">'authorization'</span>];
  <span class="hljs-keyword">if</span> (!authorization) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unauthorized'</span>);

  <span class="hljs-comment">// Extract the user from the authorization header. This is for example</span>
  <span class="hljs-comment">// purposes only. In a real application, you would use a JWT library or</span>
  <span class="hljs-comment">// better authentication methods in your API.</span>
  <span class="hljs-keyword">const</span> user = authorization.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];
  <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unauthorized'</span>);

  <span class="hljs-comment">// Extract the resource from the path. This is for example purposes only.</span>
  <span class="hljs-keyword">let</span> resource = path.split(<span class="hljs-string">'/'</span>).reverse()[<span class="hljs-number">0</span>]; <span class="hljs-comment">// get the last part of the path</span>
  resource = resource.slice(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>); <span class="hljs-comment">// remove the trailing 's' </span>
  <span class="hljs-comment">// Capitalize the first letter</span>
  resource = resource.charAt(<span class="hljs-number">0</span>).toUpperCase() + resource.slice(<span class="hljs-number">1</span>);

  <span class="hljs-comment">// Set the action on the resource from the request method. </span>
  <span class="hljs-comment">// This is for example purposes only. In a real application, you would</span>
  <span class="hljs-comment">// have a more robust way to determine the action. </span>
  <span class="hljs-keyword">const</span> action = method === <span class="hljs-string">'POST'</span> ? <span class="hljs-string">'create'</span> : <span class="hljs-string">'delete'</span>;

  <span class="hljs-comment">// Construct the Permit object. Use the token from runtime config.</span>
  <span class="hljs-keyword">const</span> config = useRuntimeConfig(event);
  <span class="hljs-keyword">const</span> permit = <span class="hljs-keyword">new</span> Permit({
    pdp: <span class="hljs-string">'https://cloudpdp.api.permit.io'</span>,
    token: config.permitToken
  });

  <span class="hljs-comment">// Check if the user is permitted to create the resource. </span>
  <span class="hljs-comment">// If not, throw an error.</span>
  <span class="hljs-keyword">const</span> isPermitted = <span class="hljs-keyword">await</span> permit.check(user, action, resource);
  <span class="hljs-keyword">if</span> (!isPermitted) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unauthorized'</span>);
});
</code></pre>
<p>As you can see, if the Permit checker fails, an error will be thrown. This will make Nuxt to prevent unauthorized resource management in your system. Such separation of concerns is efficient, especially in Authorization.</p>
<h2 id="heading-how-to-test-rbac-in-the-community-dashboard">How to Test RBAC in the Community Dashboard</h2>
<p>The settings page works together with the <code>stores/permissions.ts</code> file to complete the flow in the front end. We hardcoded the roles and the “user IDs” to ease toggling and testing. You definitely won’t have this in a production application. You can <a target="_blank" href="https://docs.permit.io/integrations/feature-flagging/casl/">integrate CASL for permission checks in a frontend</a>.</p>
<p>In this demo community dashboard, the UI touchpoints only allow edits of entities for which the acting user has the right roles. In other words, you can only add or delete an entity if the current role in the settings page allows that. Let’s see this in action.</p>
<p>In the Permit.io dashboard, create three test users: “example-member”, “example-mentor”, and “example-admin”. Assign the respective roles to each user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037709104/0cb7b666-8597-4839-802b-e79e0434572a.gif" alt="Demo of Creating Test Users and assigning Roles in Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>Start up the Nuxt app by running <code>npm run dev</code>. Visit <code>localhost:3000</code> in your browser and explore the role-based authorization in the demo community dashboard.</p>
<p>You can see that when you set the Current User to admin, you can create and delete announcements, but when set to guest, you can only view entities and not manage them. With this, we’ve fully implemented authorization.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732031066634/54310991-3ce9-4736-8735-c2776e4f6d82.gif" alt="Demo of Testing Roles in the Community Dashboard" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<h2 id="heading-summary">Summary</h2>
<p>You can be more efficient in building your application by focusing on business logic and outsourcing crucial parts like authorization.</p>
<p>In this article, you learned how you can use an authorization solution (Permit.io) to implement Role-Based Access Control in a demo community dashboard with Nuxt. You can also use Permit in any other kind of application (not just community dashboards).</p>
<p>When planning authorization, define resources alongside the <em>“</em>actions” users can execute on each resource. After that, define roles as the permissions the users can have.</p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Web Apps with Nuxt and Laravel ]]>
                </title>
                <description>
                    <![CDATA[ The Laravel framework is one of the most widely used technologies in the web development ecosystem. It's relatively straightforward, and it's easy to use for building websites.  Laravel is built upon PHP, a popular web programming language that’s use... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-web-apps-with-nuxt-and-laravel/</link>
                <guid isPermaLink="false">66b9d9c738655574e9f95267</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Applications ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Abdulrahman Yusuf ]]>
                </dc:creator>
                <pubDate>Wed, 07 Feb 2024 10:59:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/pexels-pixabay-270557.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The Laravel framework is one of the most widely used technologies in the web development ecosystem. It's relatively straightforward, and it's easy to use for building websites. </p>
<p>Laravel is built upon PHP, a popular web programming language that’s used in over 75% of websites on the web. So knowing how to use a framework like Laravel can help make you a sought-after developer – and it also makes building websites and applications more seamless. </p>
<p>Nuxt is a Vue.js framework used for building rich and interactive web applications. It lets you choose between different rendering modes depending on the application requirements you want to build. You can choose between building a fully server-rendered app or client-rendered app. Nuxt also offers a mixture of both rendering modes, making applications much more powerful, efficient, and interactive.</p>
<p>In this article, you'll learn how to build full-stack applications using Nuxt and Laravel by building a Book Library App. The app will comprise a library API that we'll build using Laravel and a frontend using Nuxt. </p>
<p>We’ll talk about:</p>
<ul>
<li>Installing and setting up Laravel</li>
<li>Creating database models</li>
<li>Migrations</li>
<li>Controllers</li>
<li>API Testing</li>
<li>Form Validations</li>
<li>Data Fetching in Nuxt</li>
</ul>
<p>And more. Get ready, and let's dive in.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-laravel-on-your-machine">How to set up Laravel on your machine</a></li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-book-library-api">How to build the Book Library API</a></p>
<ul>
<li><a class="post-section-overview" href="#setting-up-our-database">Setting up our database</a></li>
<li><a class="post-section-overview" href="#creating-our-book-model">Creating our Book Model</a></li>
<li><a class="post-section-overview" href="#creating-our-book-controller">Creating our Book Controller</a></li>
<li><a class="post-section-overview" href="#defining-our-api-routes">Defining our API Routes</a></li>
<li><a class="post-section-overview" href="#testing-our-api">Testing our API</a><ul>
<li><a class="post-section-overview" href="#heading-creating-a-new-book-1">Creating a new book</a></li>
<li><a class="post-section-overview" href="#getting-our-list-of-books">Getting our list of books</a></li>
<li><a class="post-section-overview" href="#editing-our-book">Editing our book</a></li>
</ul>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-nuxt-frontend">How to Build the Nuxt Frontend</a></p>
</li>
<li><a class="post-section-overview" href="#heading-how-to-integrate-the-laravel-api-in-the-frontend">How to Integrate the Laravel API in the Frontend</a><ul>
<li><a class="post-section-overview" href="#getting-all-books-in-our-library">Getting all books in our library</a></li>
<li><a class="post-section-overview" href="#heading-creating-a-new-book-1">Creating a new book</a></li>
<li><a class="post-section-overview" href="#heading-editing-a-book">Editing a book</a></li>
<li><a class="post-section-overview" href="#heading-deleting-a-book">Deleting a book</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
<li><a class="post-section-overview" href="#heading-resources">Resources</a></li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<ol>
<li><a target="_blank" href="https://www.php.net">PHP</a> and <a target="_blank" href="https://getcomposer.org">Composer</a> are installed on your local machine.</li>
<li><a target="_blank" href="https://nodejs.org/">Node.js</a> is installed on your local machine.</li>
<li><a target="_blank" href="https://yarnpkg.com/">yarn</a> or <a target="_blank" href="https://www.npmjs.com">npm</a> installed on your local machine (npm comes pre-installed with Node).</li>
<li>A text editor installed, like <a target="_blank" href="https://code.visualstudio.com">VSCode</a>.</li>
<li>Basic knowledge of HTML, CSS, JavaScript, and the terminal.</li>
<li>Basic knowledge of PHP, Vue.js, and TypeScript.</li>
</ol>
<h2 id="heading-how-to-set-up-laravel-on-your-machine"><strong>How to Set Up Laravel on Your Machine</strong></h2>
<p>To begin the Laravel installation, open your terminal and bootstrap a new Laravel project using the command below: </p>
<pre><code class="lang-bash">composer create-project laravel/laravel library-api &amp;&amp; <span class="hljs-built_in">cd</span> library-api &amp;&amp; code .
</code></pre>
<p>This command creates a new Laravel project in your directory. <code>cd</code>'s into it, and open up VSCode using the <code>code .</code> command. (If you happen to be using a different editor, you can remove that command and open the directory manually).</p>
<p>To test your Laravel server and ensure everything works, let’s test the server by using the <code>php artisan serve</code> command in the terminal. This should make your API available on port 8000 and accessible in your browser.</p>
<p><img src="https://lh7-us.googleusercontent.com/Wd1K8FLTnBwThyGwMl8lYeTmpO2886t4IN2lJ6Nv2tShwvw0HGxNWhl3cneeAEVVdVL_Gvf9sB0feecPHqpRpXYbPz-dBRPPAnBxJAabWyMySW-FqSwJkUi1_bTOX7fLqo1luWJIRi1iEPlbSkVxh3U" alt="Image" width="1600" height="865" loading="lazy">
<em>Laravel Starter Application</em></p>
<h2 id="heading-how-to-build-the-book-library-api"><strong>How to Build the Book Library API</strong></h2>
<p>We will be creating a simple CRUD endpoint for our book library, that is the API should be able to achieve the following:</p>
<ul>
<li>Create new book entries</li>
<li>Get a list of all the book entries in the database</li>
<li>Edit previously added book entries</li>
<li>Delete book entries from the database</li>
</ul>
<h3 id="heading-setting-up-the-database">Setting up the database</h3>
<p>Laravel comes with a variety of databases you can use during development. By default, it automatically creates a configuration setup for MySQL. But for simplicity, we’ll be making use of a <a target="_blank" href="https://www.sqlite.org/index.html">SQLite</a> database in this tutorial. </p>
<p>Open the <code>.env</code> file in your root directory, change the <code>DB_CONNECTION</code> value from <code>mysql</code> to <code>sqlite</code>, and comment the remaining database configurations as you can see below:</p>
<p><img src="https://lh7-us.googleusercontent.com/NoxsSV5jfFxxzAsKDX04xAfWzUAESGajpJnam3zTA3LpBIil9-esqfxk4I1CJXjlK_jyjEk3twDTGu_Bi_Efw1DUXaU0yUcdCFcdKECCZ-V-hT4Y_03YKfeQCz8JQ5MVSznXcC3dYei8vBb9wTCo1_c" alt="Image" width="1018" height="294" loading="lazy">
<em>Environmental Variables (available in .env file)</em></p>
<p>Laravel automatically helps create the SQLite database when the migrations command is run. We’ll do that in a bit after creating our database models.</p>
<h3 id="heading-creating-the-book-model"><strong>Creating the Book model</strong></h3>
<p>When you're building backend APIs, a model acts as a template that’s used to set up your database tables. It contains high-level instructions for how the tables and columns should be created. </p>
<p>In Laravel, you can use the command below to create a <code>Book</code> model:</p>
<pre><code class="lang-bash">php artisan make:model Book -m
</code></pre>
<p>This creates your Book model in the <code>app/models</code> directory and also creates a new migration file for you in the <code>database/migrations</code> directory.</p>
<p>Navigate to the migrations directory and open the newly created migrations file. It should be in the format <code>current_date_create_books_table.php</code> like you can see below:</p>
<p><img src="https://lh7-us.googleusercontent.com/zW84PQRgiGuLMX9yvmCJ_NvDS4xZfraKtxh7g0Du6rhOoqSpuXGlWnxkCSlTT9yhqT3c6N_jfPAsiuKakLG_MULYgYD6_Qe-Eycxo4hxaozonEVzbjVe64ddSu2FOpdOGMexms1jPuSZc43HB-N0-hY" alt="Image" width="1600" height="1016" loading="lazy">
<em>Default Migrations</em></p>
<p>Edit the <code>up</code> function and add the following content:</p>
<pre><code class="lang-php">$table-&gt;string(<span class="hljs-string">'title'</span>);
$table-&gt;string(<span class="hljs-string">'author'</span>);
$table-&gt;string(<span class="hljs-string">'isbn'</span>);
$table-&gt;date(<span class="hljs-string">'published_date'</span>);
$table-&gt;text(<span class="hljs-string">'cover_image'</span>)-&gt;nullable();
$table-&gt;foreignIdFor(User::class);
</code></pre>
<p>Your migrations file should look like this now:</p>
<p><img src="https://lh7-us.googleusercontent.com/sgSaSbourHadcXleC2Jxn8DHAkulytaRIQfcAZC7ZUPvNTJJITJDr9rDEeYSNf-mNjJxxW285Kz1C5MQlFWBNlSMG7Gk0VEIShz8-AtaA6hRHChRuYYkrYQkIl9IwZ_I0-tO7cXMFd8f3OLen-bAm9o" alt="Image" width="1600" height="1240" loading="lazy">
<em>Updated Migrations</em></p>
<p>In summary, you added new fields to be created for the Book tables. <code>Title</code>, <code>author</code>, and <code>isbn</code> all have a data type of string, while <code>published_date</code> and <code>cover_image</code> have data types of date and text, respectively. </p>
<p>You also made the <code>cover_image</code> nullable, that is the field can be left out whenever a new book entry is created. </p>
<p>Lastly, you imported the <code>User</code> class and made it a <a target="_blank" href="https://www.educative.io/blog/what-is-foreign-key-database">foreign key</a> to your Book table, creating a many-to-one relationship with the User table.</p>
<p>Now, let’s create your tables by running the migrations file with the command below:</p>
<pre><code class="lang-php">php artisan migrate
</code></pre>
<p>You should get a prompt in the terminal asking if you want to create your SQLite database before the migrations. Select yes and press enter to proceed with the migrations.</p>
<p>Once the migration has been run successfully, navigate to the Book model and paste in the following code:</p>
<pre><code class="lang-php"> <span class="hljs-comment">/**
* The attributes that are mass assignable.
*
* <span class="hljs-doctag">@var</span> array&lt;int, string&gt;
*/</span>

<span class="hljs-keyword">protected</span> $fillable = [
    <span class="hljs-string">'user_id'</span>,
    <span class="hljs-string">'title'</span>,
    <span class="hljs-string">'author'</span>,
    <span class="hljs-string">'isbn'</span>,
    <span class="hljs-string">'published_date'</span>,
    <span class="hljs-string">'cover_image'</span>
];
</code></pre>
<p>This will allow mass assignment when passing data into the model (passing the data as a whole instead of individually). Your Book model should look like this now:</p>
<p><img src="https://lh7-us.googleusercontent.com/zjYcbtyiQXKFRJubxnDuL5M6PRyEI5u5PaHGHUAfYgju5A8lQqOJAxRSLdsaIkg0bjFCXOOUTBGHlQXuJhpuEfOW5K6XbPDl2aEQoUyNvjs-p-m5TfRQhF2dlnwnZKHDb0EXsR6JneIGIXCw0ol1plI" alt="Image" width="1600" height="779" loading="lazy">
<em>Updated Book Model</em></p>
<h3 id="heading-creating-the-book-controller"><strong>Creating the Book controller</strong></h3>
<p>The controller is responsible for housing the business logic for any API being created. The implementation logic for how we want to perform any CRUD operations with the library API will live here. </p>
<p>Laravel also provides a command for you to make this easier for developers. Paste the command below in your terminal to create our book controller:</p>
<pre><code class="lang-php">php artisan make:controller BookController
</code></pre>
<p>This should be accessible at <code>app\Http\Controllers\BookController.php</code>. Replace the content of your BookController class with the code snippet below:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Book</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Carbon</span>\<span class="hljs-title">Carbon</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Log</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Storage</span>;


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">Request $request</span>)
    </span>{
        $data = $request-&gt;validate([
            <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'required|string'</span>,
            <span class="hljs-string">'author'</span> =&gt; <span class="hljs-string">'required|string'</span>,
            <span class="hljs-string">'isbn'</span> =&gt; <span class="hljs-string">'required|string|unique:books'</span>,
            <span class="hljs-string">'published_date'</span> =&gt; <span class="hljs-string">'required|string'</span>,
            <span class="hljs-string">'cover_image'</span> =&gt; <span class="hljs-string">'nullable|image|max:2048'</span>,
        ]);

        <span class="hljs-keyword">try</span> {
            $published_date = Carbon::parse($data[<span class="hljs-string">'published_date'</span>])-&gt;toDateString();
            $data[<span class="hljs-string">'published_date'</span>] = $published_date;
            $data[<span class="hljs-string">'user_id'</span>] = <span class="hljs-number">1</span>; <span class="hljs-comment">// Making the assumption the id of the user is 1 </span>
        } <span class="hljs-keyword">catch</span> (\<span class="hljs-built_in">Exception</span> $e) {
            <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'Error'</span> =&gt; <span class="hljs-string">'Bad Request'</span>], <span class="hljs-number">400</span>);
        }

        $book = <span class="hljs-keyword">new</span> Book($data);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'cover_image'</span>)) {
            $coverImage = $request-&gt;file(<span class="hljs-string">'cover_image'</span>);
            $coverImageName = time() . <span class="hljs-string">'.'</span> . $coverImage-&gt;getClientOriginalExtension();
            $coverImage-&gt;move(public_path(<span class="hljs-string">'images'</span>), $coverImageName);
            $book-&gt;cover_image = $coverImageName;
        }

        $book-&gt;save();

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Book added successfully'</span>], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
    </span>{
        $books = Book::all();

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'books'</span> =&gt; $books], <span class="hljs-number">200</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params">Request $request, $id</span>)
    </span>{
        $book = Book::findOrFail($id);
        $data = $request-&gt;all();

        <span class="hljs-keyword">try</span> {
            $published_date = Carbon::parse($data[<span class="hljs-string">'published_date'</span>])-&gt;toDateString();
            $data[<span class="hljs-string">'published_date'</span>] = $published_date;
            $data[<span class="hljs-string">'user_id'</span>] = $book-&gt;user_id;
        } <span class="hljs-keyword">catch</span> (\<span class="hljs-built_in">Throwable</span> $th) {
            Log::error(<span class="hljs-string">'Error '</span>, $th);
            <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'Error'</span> =&gt; <span class="hljs-string">'Bad Request'</span>], <span class="hljs-number">400</span>);
        }

        $book-&gt;update($data);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'cover_image'</span>)) {
            <span class="hljs-comment">// Delete the previous cover image if it exists</span>
            <span class="hljs-keyword">if</span> ($book-&gt;cover_image) {
                Storage::delete(<span class="hljs-string">'public/images/'</span> . $book-&gt;cover_image);
            }

            $coverImage = $request-&gt;file(<span class="hljs-string">'cover_image'</span>);
            $coverImageName = time() . <span class="hljs-string">'.'</span> . $coverImage-&gt;getClientOriginalExtension();
            $coverImage-&gt;storeAs(<span class="hljs-string">'public/images'</span>, $coverImageName);
            $book-&gt;cover_image = $coverImageName;
            $book-&gt;save();
        }

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Book updated successfully'</span>], <span class="hljs-number">200</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">destroy</span>(<span class="hljs-params">$id</span>)
    </span>{
        $book = Book::findOrFail($id);

        <span class="hljs-comment">// Delete the cover image if it exists</span>
        <span class="hljs-keyword">if</span> ($book-&gt;cover_image) {
            Storage::delete(<span class="hljs-string">'public/images/'</span> . $book-&gt;cover_image);
        }

        $book-&gt;delete();

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Book deleted successfully'</span>], <span class="hljs-number">200</span>);
    }
}
</code></pre>
<h3 id="heading-defining-the-api-routes"><strong>Defining the API Routes</strong></h3>
<p>Routing is one of the most important parts of building an API. Routes serve as access points through which developers, clients, or any other services can access an API. </p>
<p>The Laravel router allows you to register any routes that correspond to any HTTP verb being requested.</p>
<pre><code class="lang-php">Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
</code></pre>
<p>Navigate to the <code>routes/api.php</code> file and create the following routes for your API:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/books'</span>, [BookController::class, <span class="hljs-string">'store'</span>]);
Route::get(<span class="hljs-string">'/books'</span>, [BookController::class, <span class="hljs-string">'index'</span>]);
Route::put(<span class="hljs-string">'/books/{id}'</span>, [BookController::class, <span class="hljs-string">'update'</span>]);
Route::delete(<span class="hljs-string">'/books/{id}'</span>, [BookController::class, <span class="hljs-string">'destroy'</span>]);
</code></pre>
<h3 id="heading-testing-the-api"><strong>Testing the API</strong></h3>
<p>Now, it’s time to test your API and ensure all your endpoints are accessible and working as expected. </p>
<p>Run the Laravel server using the command below:</p>
<pre><code class="lang-php">php artisan serve
</code></pre>
<p>You can test the API using any API client of your choice. We have quite a number of popular options to choose from – <a target="_blank" href="https://www.postman.com">Postman</a>, <a target="_blank" href="https://insomnia.rest">Insomnia</a>, <a target="_blank" href="https://www.thunderclient.com">Thunder Client</a>, and so on. I’ll be using Thunder Client here.</p>
<p>We’ll test for all CRUD operations in your API client by confirming if all your objectives for your API are met.</p>
<h3 id="heading-creating-a-new-book">Creating a new book</h3>
<p><img src="https://lh7-us.googleusercontent.com/pUy0u6hCbOEihAsLhLTtSS6p0l3a42PIKHceWRnq5m4-0MJHwwG2r4mL5Y8MCr_EG3g7AuvvmW6nEx9SZ4-GtHTQNFO5Som8f4PYBPjntJSTNJXzrV0sbjL9RYRGhILUWHo6eWFb9tX82XWmrwH70yI" alt="Image" width="1600" height="1096" loading="lazy">
<em>Create book endpoint</em></p>
<h3 id="heading-getting-a-list-of-books">Getting a list of books</h3>
<p><img src="https://lh7-us.googleusercontent.com/9QNUQRrxFzime9Dsl9bKuh82o0XzD6AY8fUaAmQB_mHP0-pyqviLOVPfesLUsINl9n54tYqbjPNOWYEkJwfoXkaVuREbjGhqhghYI5ifi8e7TTQrNSIIm4jU3rM_8akLtc4so-4AzeegJicMpL8IGZw" alt="Image" width="1600" height="1095" loading="lazy">
<em>Get books endpoint</em></p>
<h3 id="heading-editing-a-book">Editing a book</h3>
<p><img src="https://lh7-us.googleusercontent.com/ZTC04dUEXPF2BdxNH-l9RbfzjhkMqO338pQSHjcoEl_hfa3EZbRUtWS2Stgk7slAlwjXpl1_ypvALbuQX3AvLaDPO0gj2rzh5BpqO-uwK6yjSehztm1wJnrjWLzKM0-asFqOU2wMOTp1xffqVroewec" alt="Image" width="1600" height="939" loading="lazy">
<em>Edit book endpoint</em></p>
<h2 id="heading-how-to-build-the-nuxt-frontend">How to Build the Nuxt Frontend</h2>
<p>Setting up a new Nuxt app is as simple as running the command below:</p>
<pre><code class="lang-bash">npx nuxi@latest init nuxt-library-frontend
</code></pre>
<p>Open the newly created project when you’re done installing all the dependencies and start the development server using this command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Your Nuxt app should now be accessible on port 3000:</p>
<p><img src="https://lh7-us.googleusercontent.com/ZOV-VAcrLLTW7i6SAmFNh5q8hRcxhY05wC4vZUXbxTwgR5tTsKop-An1SYlFdXEiquu1zCz1tx5TTwrY9WlDEPuiuNpHh4vDsVWwlUVGvZFWljmsTGgRYST3478Pk79jNcNeXJoGa7lQcAdbTx3YitI" alt="Image" width="1600" height="873" loading="lazy">
<em>Nuxt Starter Page</em></p>
<p>This is what your frontend should look like at the end of the tutorial:</p>
<p><img src="https://lh7-us.googleusercontent.com/FbnijAJw1D-VJTnT8VNPQBfl694-fW2PhYTqoEokTMxceEnsN5Oa-mBTmSAcoeU8xcqRFyDceWTQjElj43oidAg2xtW2KWSVamDbL0kmVDuCZktQc1mER5pbCbGkshNsYm0WJKqbgQy8GVymZ8-Bo5I" alt="Image" width="1600" height="871" loading="lazy">
<em>Sample of our completed application</em></p>
<p>I’ve created some <a target="_blank" href="https://github.com/Young-Einstein10/laravel-nuxt-app">boilerplate code</a> to help with the Nuxt frontend setup. This will enable us to focus on the implementation logic for consuming the Laravel API rather than spending time setting things up and styling. </p>
<p>For knowledge's sake, I created the UI components for the app using <a target="_blank" href="https://www.shadcn-vue.com">shadcn-vue</a>. It consists of an amazing collection of accessible, reusable UI components that you can customize to your taste. Check out the <a target="_blank" href="https://www.shadcn-vue.com/docs/installation/nuxt.html">installation</a> steps for more details on how easy it was to set up for the Nuxt app. </p>
<p>It also comes with an installation of the <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> utility library that’ll we'll use for styling in this tutorial.</p>
<p>Clone the boilerplate setup from the GitHub repo <a target="_blank" href="https://github.com/Young-Einstein10/laravel-nuxt-app">here</a> so we can get started.</p>
<p>Navigate to <code>pages/index.vue</code> and replace the content of the index page with this:</p>
<pre><code class="lang-vue">&lt;template&gt;
 &lt;main class="bg-white p-10 min-h-screen"&gt;
   &lt;div class="max-w-4xl mx-auto"&gt;
     &lt;header class="flex justify-between items-center mb-20"&gt;
       &lt;h1 class="font-semibold text-4xl"&gt;My Library&lt;/h1&gt;
       &lt;Button&gt;Add New Book&lt;/Button&gt;
     &lt;/header&gt;


     &lt;div v-if="isLoading"&gt;
       &lt;p class="italic text-2xl font-medium text-center"&gt;Loading...&lt;/p&gt;
     &lt;/div&gt;
     &lt;div class="mt-8"&gt;
       &lt;div class="flex flex-col gap-8"&gt;
         &lt;div class="flex gap-4" v-for="book in books"&gt;
           &lt;div
             v-if="book.cover_image"
             class="rounded-lg w-32 h-44 flex items-center justify-center"
           &gt;
             &lt;NuxtImg
               :src="`${API_BASE_URL}/images/${book.cover_image}`"
               :alt="book.title"
               class="w-full h-auto"
             /&gt;
           &lt;/div&gt;
           &lt;div v-else class="bg-slate-300 rounded-lg w-32 h-44"&gt;&lt;/div&gt;
           &lt;div class="flex items-center justify-between flex-1"&gt;
             &lt;div&gt;
               &lt;h3 class="font-medium text-xl mb-4"&gt;{{ book.title }}&lt;/h3&gt;
               &lt;p class="mb-2"&gt;
                 &lt;span&gt;by:&lt;/span&gt;
                 &lt;span class="italic"&gt; {{ book.author }} &lt;/span&gt;
               &lt;/p&gt;
               &lt;p&gt;
                 &lt;span&gt;Published:&lt;/span&gt;
                 {{ book.published_date }}
               &lt;/p&gt;
             &lt;/div&gt;
             &lt;div class="actions flex gap-4"&gt;
               &lt;Button&gt;Edit&lt;/Button&gt;
               &lt;Button variant="destructive"&gt; Delete &lt;/Button&gt;
             &lt;/div&gt;
           &lt;/div&gt;
         &lt;/div&gt;
       &lt;/div&gt;
     &lt;/div&gt;
   &lt;/div&gt;
 &lt;/main&gt;
&lt;/template&gt;


&lt;script lang="ts" setup&gt;
import { API_BASE_URL } from "@/utils/constants";
import type { BookProps } from "@/utils/types";


const isLoading = ref(false);
const books = ref&lt;BookProps[]&gt;([]);
&lt;/script&gt;
</code></pre>
<p>We’ll need to create a new folder called <code>utils</code> in your root project which will contain two files: <code>constants.ts</code> and <code>types.ts</code>. Paste the following code snippet in your <code>constants.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> API_BASE_URL = <span class="hljs-string">"http://localhost:8000"</span>;
</code></pre>
<p>And the following in your <code>types.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> BookProps {
     id?: <span class="hljs-built_in">number</span>;
     title: <span class="hljs-built_in">string</span>;
     author: <span class="hljs-built_in">string</span>;
     isbn: <span class="hljs-built_in">string</span>;
     published_date: <span class="hljs-built_in">string</span>;
     cover_image?: <span class="hljs-built_in">string</span>;
     user_id?: <span class="hljs-built_in">number</span>;
     created_at?: <span class="hljs-built_in">string</span>;
     updated_at?: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>Save the <code>pages/index.vue</code> file. You should be able to see this result in your browser:</p>
<p><img src="https://lh7-us.googleusercontent.com/1M7J0_i-u7rUaHr6vX5na1sReyIh1e1s5VGFh1mZteE0j7sXM5lFUWBmPXZNDbLuobYM9nkJtodCvUTZig22KnJS471YHdOowjXmlmMBwkBuYpQ0J10rgi_xmJt8Vyfm_tHqsTEh6wdtqLx1q1X7KDc" alt="Image" width="1600" height="871" loading="lazy">
<em>Scaffolding the library frontend with Nuxt</em></p>
<p>Now, we need to create a side drawer and a dialog for adding new books and deleting added books from the database, respectively. </p>
<p>Create a new file called <code>BookDrawer.vue</code> in the component's root and add the following code in there:</p>
<pre><code class="lang-vue">&lt;script setup lang="ts"&gt;
import type { BookProps } from "@/utils/types";


type PickedProps = "title" | "author" | "isbn" | "published_date";
interface CustomBookProps extends Pick&lt;BookProps, PickedProps&gt; {
 cover_image?: string;
}


const props = defineProps&lt;{ open: boolean; book?: BookProps }&gt;();
const emit = defineEmits(["update:open", "refresh-data"]);


const isSubmitting = ref(false);
let form = reactive&lt;CustomBookProps&gt;({
 title: "",
 author: "",
 isbn: "",
 published_date: "",
 cover_image: undefined,
});


const onFileChange = async (e: any) =&gt; {
 form.cover_image = e.target.files[0];
};


const onSubmit = async () =&gt; {};


const closeDrawer = (openState: boolean) =&gt; emit("update:open", openState);
&lt;/script&gt;


&lt;template&gt;
 &lt;Sheet :open="open" @update:open="closeDrawer"&gt;
   &lt;SheetContent class="w-full bg-white"&gt;
     &lt;SheetHeader&gt;
       &lt;SheetTitle&gt;
         &lt;template v-if="book"&gt; Edit Book &lt;/template&gt;
         &lt;template v-else&gt;Add New Book&lt;/template&gt;
       &lt;/SheetTitle&gt;
       &lt;SheetDescription&gt;
         Make changes to your profile here. Click save when you're done.
       &lt;/SheetDescription&gt;
     &lt;/SheetHeader&gt;
     &lt;div class="grid gap-4 py-4"&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="bookTitle" class="text-right"&gt; Book Title &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.title"
           type="text"
           id="bookTitle"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="author" class="text-right"&gt; Author &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.author"
           type="text"
           id="author"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="isbn" class="text-right"&gt; ISBN &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.isbn"
           type="text"
           id="isbn"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="published_date" class="text-right"&gt;
           Published Date
         &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.published_date"
           type="date"
           id="published_date"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="cover_image" class="text-right"&gt; Book Cover &lt;/Label&gt;


         &lt;Input
           type="file"
           @change="onFileChange"
           id="cover_image"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
     &lt;/div&gt;
     &lt;SheetFooter&gt;
       &lt;Button type="submit" @click="onSubmit"&gt;
         &lt;template v-if="isSubmitting"&gt;Saving...&lt;/template&gt;
         &lt;template v-else&gt; Save changes &lt;/template&gt;
       &lt;/Button&gt;
     &lt;/SheetFooter&gt;
   &lt;/SheetContent&gt;
 &lt;/Sheet&gt;
&lt;/template&gt;
</code></pre>
<p>Create another file called <code>DeleteBookDrawer.vue</code> and add the following code content to it: </p>
<pre><code class="lang-vue">&lt;script lang="ts" setup&gt;
const emit = defineEmits(["refresh-data", "update:open"]);
const props = defineProps&lt;{ open: boolean; book?: BookProps }&gt;();
&lt;/script&gt;


&lt;template&gt;
 &lt;AlertDialog
   :open="open"
   @update:open="(openState) =&gt; $emit('update:open', openState)"
 &gt;
   &lt;AlertDialogContent&gt;
     &lt;AlertDialogHeader&gt;
       &lt;AlertDialogTitle&gt;Are you absolutely sure?&lt;/AlertDialogTitle&gt;
       &lt;AlertDialogDescription&gt;
         This action cannot be undone. This will permanently delete your book
         and remove your data from the server.
       &lt;/AlertDialogDescription&gt;
     &lt;/AlertDialogHeader&gt;
     &lt;AlertDialogFooter&gt;
       &lt;AlertDialogCancel @click="$emit('update:open', false)"
         &gt;Cancel&lt;/AlertDialogCancel
       &gt;
       &lt;AlertDialogAction&gt;Continue&lt;/AlertDialogAction&gt;
     &lt;/AlertDialogFooter&gt;
   &lt;/AlertDialogContent&gt;
 &lt;/AlertDialog&gt;
&lt;/template&gt;
</code></pre>
<p>Import the newly created files in your index page as you can see below:</p>
<pre><code class="lang-vue">&lt;template&gt;
 &lt;main class="bg-white p-10 min-h-screen"&gt;
    ...

   &lt;BookDrawerDialog
     v-if="isBookDrawerOpen"
     :open="isBookDrawerOpen"
     @update:open="(open: boolean) =&gt; isBookDrawerOpen = open"
     :book="currentBook"
     @refresh-data="fetchBooks"
   /&gt;
   &lt;DeleteBookDialog
     v-if="isDeleteBookDialogOpen"
     :open="isDeleteBookDialogOpen"
     @update:open="(open: boolean) =&gt; isDeleteBookDialogOpen = open"
     :book="currentBook"
     @refresh-data="fetchBooks"
   /&gt;
 &lt;/main&gt;
&lt;/template&gt;
</code></pre>
<p>Now, we need to create a system for opening and closing the drawer and dialog, respectively. We’ll be creating a <a target="_blank" href="https://vuejs.org/guide/essentials/reactivity-fundamentals.html#ref">ref</a> to track the open/closed state for the drawer and dialog components in your index page. Paste the following code content in the script stage to enable that:</p>
<pre><code class="lang-vue">&lt;script lang="ts" setup&gt;

...

const currentBook = ref&lt;BookProps&gt;();


const isBookDrawerOpen = ref(false);
const isDeleteBookDialogOpen = ref(false);


const toggleAddBookDrawer = () =&gt; {
 isBookDrawerOpen.value = !isBookDrawerOpen.value;
};


const toggleEditBookDrawer = (book: BookProps) =&gt; {
 currentBook.value = book;
 isBookDrawerOpen.value = !isBookDrawerOpen.value;
};


const toggleDeleteDialog = (book: BookProps) =&gt; {
 currentBook.value = book;
 isDeleteBookDialogOpen.value = !isDeleteBookDialogOpen.value;
};
&lt;/script&gt;
</code></pre>
<p>Update the <code>Edit</code> and <code>Delete</code> buttons in your index page to include the toggle handler:</p>
<pre><code class="lang-vue">    ...

             &lt;div class="actions flex gap-4"&gt;
               &lt;Button @click="toggleEditBookDrawer(book)"&gt;Edit&lt;/Button&gt;
               &lt;Button @click="toggleDeleteDialog(book)" variant="destructive"&gt;
                 Delete
               &lt;/Button&gt;
             &lt;/div&gt;

           ...
</code></pre>
<p>Include the <code>toggleAddDrawer</code> in your Add Book button too:</p>
<pre><code class="lang-vue">&lt;Button @click="toggleAddBookDrawer"&gt;Add New Book&lt;/Button&gt;
</code></pre>
<p>Save the file changes and view in your browser. You should have the drawer active now:  </p>
<p><img src="https://lh7-us.googleusercontent.com/82AHDkwCX-oTW6pOpcwi735-8i5HluzloFnwDGnidH5gNKP8dkj3pXxr-Oa6gpMcQGZA-FnzsCP5nzpP0uGPDMhyTMkNqvxLXnVaK0TWFTBG-IrQPGU74-vhPpqCz1y7CcRULG7AUWcJsmejfdR5O2k" alt="Image" width="1600" height="816" loading="lazy">
<em>Add New Book Drawer</em></p>
<p>Moving on, we need to be sure the EditDrawer and Delete Dialog work. We’ll create a temporary array of mock book data to test that so you can see the result on your page. Update the value of the <code>books</code> ref to the data you have below (and don’t forget to update the books ref value to an empty array – that is <code>books = ref&lt;BookProps&gt;([])</code>):</p>
<pre><code class="lang-vue">const books = ref&lt;BookProps[]&gt;([
     {
       title: "Atomic Habits",
       author: "James Clear",
       isbn: "XYEOUIUEHEJ2902",
       published_date: "2020-09-01",
       cover_image: "",
     },
     {
       title: "The Power of Habits",
       author: "Charles Duhigg",
       isbn: "WIUIQUEWHSDBSD28",
       published_date: "2012-02-28",
       cover_image: "",
     },
]);
</code></pre>
<p>Save the changes and check out the EditDrawer and DeleteDialog in action:</p>
<p><img src="https://lh7-us.googleusercontent.com/_kiROvQBeLBq1UV9Zj4n0tVYOdW9nLUUjb5qlxGtgFGvAJtHsGJ0CvoEyYHxB4BIQ31SznXfwkiXhg_5VowECEMuSCYnIhfEHWeiHAt-hhA9dETiyTOgqNjsxxAenIhx0gl2koYaLVGYnohdpTV13KU" alt="Image" width="1519" height="824" loading="lazy">
<em>Delete dialogue</em></p>
<p><img src="https://lh7-us.googleusercontent.com/YSTKDAEOpLp45EXn61OlRYTTW3hYmOF-ur3SES7emDWMyigINQNvpAozgscEF84-RgiZrgCkTIxtZehFYDMIntzWmy589khRcP7FV4OpdacfXvsi8xRlJG9qWBuXc8Csbal0C_Tr-_lrDt6AatSnVIg" alt="Image" width="1513" height="821" loading="lazy">
<em>EditDrawer</em></p>
<h2 id="heading-how-to-integrate-the-laravel-api-in-the-frontend">How to Integrate the Laravel API in the Frontend</h2>
<h3 id="heading-getting-all-books-in-the-library">Getting all books in the library</h3>
<p>So far, we only have the UI of our frontend working, and the data being displayed is not from the API. To make the app fully dynamic, you'll need to make an HTTP request to the server to get all the data you need for your page. </p>
<p>Update the body of the <code>fetchBooks</code> function in the index page to the following code:</p>
<pre><code class="lang-vue">const fetchBooks = async () =&gt; {
 try {
   isLoading.value = true;
   const response = await $fetch&lt;{ books: BookProps[] }&gt;(
     `${API_BASE_URL}/api/books`
   );


   books.value = response.books.sort(
     (a, b) =&gt;
       Number(new Date(b.created_at as string)) -
       Number(new Date(a.created_at as string))
   );


 } catch (error) {
   console.log(error);
 } finally {
   isLoading.value = false;
 }
};


onMounted(() =&gt; {
 fetchBooks();
});
</code></pre>
<p>Basically, you're calling your <code>fetchBooks</code> function when the page mounts using one of the <a target="_blank" href="https://vuejs.org/api/composition-api-lifecycle.html#onmounted">lifecycle hooks</a> available in Vue.js. When the function is called, it executes the instruction you created in the body:</p>
<ul>
<li>Set the loading state of the app to true.</li>
<li>Make a GET HTTP request to the books endpoint of your API and set the response to the books ref you created earlier.</li>
<li>When the request-response has been completed, set the loading state of the app back to false to indicate to the user the process has been completed.</li>
</ul>
<p>Save the changes, and you should see the results in the browser:</p>
<p><img src="https://lh7-us.googleusercontent.com/SGyND7gBGa7NmUXM4k-7Jn8pyuRizWhMJOOGnwZMMe7NIz6Jzt_qC0D3JUf-7X9IlGJMdm8XLbzGHG8cuKcJ6z83XpxOrrSGdAgOFaaUd6tDmo6-bDY-3PZG1lLRW4mywF77A86ABoIggR6NGwDDK5A" alt="Image" width="1509" height="818" loading="lazy">
<em>Fetching the book list</em></p>
<h3 id="heading-creating-a-new-book-1">Creating a new book</h3>
<p>For your book creation to be successful, you need to include the logic for creating a book in your <code>BookDrawer</code>. Add the following code in the <code>BookDrawer.vue</code> file:</p>
<pre><code class="lang-vue">...

const isSubmitting = ref(false);


const addNewBook = async (data: any) =&gt; {
 const response = await $fetch(`${API_BASE_URL}/api/books`, {
   method: "POST",
   body: data,
   headers: {
     Accept: "application/json",
   },
 });
 return response;
};


const onSubmit = async () =&gt; {
 try {
   const formData = new FormData();
   Object.keys(form).forEach((key) =&gt; {
     // @ts-ignore
     if (form[key]) {
       formData.append(key, form[key as never]);
     }
   });
   isSubmitting.value = true;
   const data = await addNewBook(formData);
   closeDrawer(false);
   emit("refresh-data");
 } catch (error) {
   console.log(error);
 } finally {
   isSubmitting.value = false;
 }
};

...
</code></pre>
<p>Similar to the implementation for fetching your books, the only difference here is that you're making a POST HTTP request instead. You use this method whenever you need to mutate data on the server.</p>
<p><img src="https://lh7-us.googleusercontent.com/7zQhG_V35mPWJW9sxMyTP8QjnezzoVg1eX08pdJ3Mw7EiXPwcHiTLdYHUD8HWqXg54SjvXymxrBCVWUjXbiuVNCAdj5vccdPyFl6KFJuuKAFTC9sYUQkkGYnESKr3aVHgUONnyOqAf1-fkafeqgyifE" alt="Image" width="1600" height="778" loading="lazy">
<em>Creating a new book</em></p>
<h3 id="heading-editing-a-book-1">Editing a book</h3>
<p>Update your <code>BookDrawer</code> with the following code to enable editing of the books that have been added:</p>
<pre><code class="lang-vue">&lt;script setup lang="ts"&gt;

...

onMounted(() =&gt; {
 if (props.book) {
   form = props.book;
 }
});


const editBook = async () =&gt; {
 try {
   isSubmitting.value = true;
   const resp = await $fetch(`${API_BASE_URL}/api/books/${props?.book?.id}`, {
     method: "PUT",
     body: formData,
   });
   closeDrawer(false);
   emit("refresh-data");
 } catch (error) {
   console.log(error);
 } finally {
   isSubmitting.value = false;
 }
};

...
&lt;/script&gt;
</code></pre>
<p>Update the save button with the toggle handler also:</p>
<pre><code class="lang-vue">&lt;Button
  type="submit"
  @click="() =&gt; (book?.id ? editBook() : onSubmit())"
&gt;
  &lt;template v-if="isSubmitting"&gt;Saving...&lt;/template&gt;
  &lt;template v-else&gt; Save changes &lt;/template&gt;
&lt;/Button&gt;
</code></pre>
<p>And here's the result:</p>
<p><img src="https://lh7-us.googleusercontent.com/aUGh8K6y9u8qXRwckj_PRphjvbnTR2DXf8jnHVehD2aaKtaCdCK1SVPjW-AbWwp_CXZROabKsTk0JBCaUjLAAzEbDHlFCbG6sjkpcVdW9VaKPqoYkVp2aoEtec5vPpjxFtNVAdFFWp3E3T_ZWNdxtYc" alt="Image" width="1600" height="867" loading="lazy">
<em>Updating a book</em></p>
<h3 id="heading-deleting-a-book">Deleting a book</h3>
<p>Paste the following in the <code>DeleteBookDialog.vue</code> file:</p>
<pre><code class="lang-vue">const refreshData = () =&gt; emit("refresh-data");
const closeDialog = () =&gt; emit("update:open", false);


const deleteBook = async () =&gt; {
 await $fetch(`${API_BASE_URL}/api/books/${props?.book?.id}`, {
   method: "DELETE",
 });
 closeDialog();
 refreshData();
};
</code></pre>
<p>This works fine also:</p>
<p><img src="https://lh7-us.googleusercontent.com/KyP9Y8L7qzkvvQCfyuFSBCQvrV30hE-rpQ72Q2Z1-DL-6rzkXnFAuwWEAL0Y1t110slYV813RPXyTKlxxKG5gHFQitUdVuT_7a-gkPxpJXtYOtRsxHWI0JSt9nFDeM6ywNf3ReoCxeIsKzguzZosw_E" alt="Image" width="1600" height="816" loading="lazy">
<em>Deleting a book</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building full-stack web apps with Laravel and Nuxt is a very rewarding experience as you can enjoy the benefits and rewards from the best of both worlds. </p>
<p>Since Laravel is a feature-rich backend framework, it comes with many modules and packages that you can install whenever a feature or implementation needs to be worked on. </p>
<p>And Nuxt, as a Vue framework, gives you the superpower of building single-page applications that are fast, accessible, and responsive, giving end-users a delightful experience.</p>
<p>In this tutorial, we went over how we can build full-stack applications using Nuxt and the Laravel framework. I hope you were able to learn something new and are excited to explore this further. Feel free to reach out if you have any questions or ideas. </p>
<p>The full code for the tutorial can be found in the GitHub repo <a target="_blank" href="https://github.com/Young-Einstein10/laravel-nuxt-app/tree/final">here</a>.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><a target="_blank" href="https://laravel.com/docs/10.x">Laravel Docs</a></li>
<li><a target="_blank" href="https://nuxt.com/docs/getting-started/installation">Nuxt Documentation</a></li>
<li><a target="_blank" href="https://www.shadcn-vue.com">Shadcn-vue</a>, ready-made UI components that can be copy-pasted anytime</li>
<li><a target="_blank" href="https://vuejs.org">Vue Docs</a>  </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Nuxt 3 Course for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ Nuxt.js has been making waves in the web development world, especially among Vue.js enthusiasts. With its unique capabilities for server-side rendering, static site generation, and single-page applications, Nuxt offers a robust set of features for de... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/nuxt-3-course-for-beginners/</link>
                <guid isPermaLink="false">66b205e9297cd6de0bd54688</guid>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 26 Sep 2023 15:37:16 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/09/nuxt.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Nuxt.js has been making waves in the web development world, especially among Vue.js enthusiasts. With its unique capabilities for server-side rendering, static site generation, and single-page applications, Nuxt offers a robust set of features for developers looking to build modern web apps.</p>
<p>We just posted a full course on Nuxt 3 on the freeCodeCamp.org YouTube channel. Guillaume Duhan created this course. Guillaume is a seasoned front-end developer with a decade of experience in the field and he is the perfect guide for those looking to learn about Nuxt.</p>
<p>Why should you learn Nuxt? Nuxt.js is a progressive framework built on top of Vue.js, designed to create modern web applications with ease. It simplifies the development process, handling tasks like routing, state management, and server-side rendering.</p>
<p>If you're building a web application that requires SEO optimization, Nuxt is a great choice due to its server-side rendering capabilities. Additionally, if you want a scalable and performance-optimized application, Nuxt's modular architecture and rich ecosystem can come in handy.</p>
<p>The course is structured to cater to both beginners and those with a foundational understanding of web development. Here's all the sections in the course:</p>
<ul>
<li>Nuxt Basics</li>
<li>Create an App</li>
<li>Pages</li>
<li>Components</li>
<li>Layouts</li>
<li>Assets</li>
<li>Composables</li>
<li>Plugins</li>
<li>Middlewares</li>
<li>Modules</li>
<li>State Management</li>
<li>Server</li>
<li>Nitro</li>
<li>Rendering modes</li>
<li>useFetch</li>
<li>useAsyncData</li>
<li>SEO &amp; Metas</li>
<li>Hooks</li>
<li>Nuxt.config.ts</li>
<li>Nuxt Content</li>
<li>Build Docs and API</li>
</ul>
<p>If you have a foundation in Vue.js or are simply curious about the capabilities of Nuxt, this comprehensive guide will provide you with the knowledge and skills to build impressive web applications.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/fTPCKnZZ2dk">the freeCodeCamp.org YouTube channel</a> (3-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/fTPCKnZZ2dk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Real-time Chat Application with Nuxt ]]>
                </title>
                <description>
                    <![CDATA[ By Idorenyin Udoh In a real-time chat application, the recipient can view the sender’s message almost immediately. This can either be in a one-on-one conversation between two parties or a group conversation.  And that's what we're going to build in t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-real-time-chat-application-with-nuxt/</link>
                <guid isPermaLink="false">66d45f31706b9fb1c166b94f</guid>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 10 Aug 2022 19:39:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/chat-app-with-nuxt-image.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Idorenyin Udoh</p>
<p>In a real-time chat application, the recipient can view the sender’s message almost immediately. This can either be in a one-on-one conversation between two parties or a group conversation. </p>
<p>And that's what we're going to build in this tutorial. For this application, we will be using <a target="_blank" href="https://nuxtjs.org/">Nuxt</a>, the intuitive Vue framework.</p>
<p>Now that we know what we’re going to be building and the technology we’ll be using, let’s go over the chat API we’ll be using.</p>
<p>For this article, we’ll be going with <a target="_blank" href="https://robinapp.io/">Robin</a> because of its minimal UI and how easy it is to integrate into our app. With that out of the way, let’s get started.</p>
<h1 id="heading-step-1-create-the-nuxt-app">Step 1 – Create the Nuxt App</h1>
<p>First, we need to create the Nuxt app with any of the following commands:</p>
<pre><code>yarn create nuxt-app nuxt-chat-app
<span class="hljs-comment">// OR</span>
npx create-nuxt-app nuxt-chat-app
<span class="hljs-comment">// OR</span>
npm init nuxt-app nuxt-chat-app
</code></pre><h1 id="heading-step-2-create-a-robin-account">Step 2 – Create a Robin Account</h1>
<p>Now that our app is ready, we need to have a Robin account before we can use it. Head over to <a target="_blank" href="https://dashboard.robinapp.co/signup">Robin’s signup page</a> to create a 30-day free trial account. </p>
<p>Robin notifies you 7 days after you've created the account and you can remove your card before the billing date. </p>
<p>After filling out the signup form, you’ll be redirected to a billing page to fill in your card information. On the next page where it redirects you, Robin requests the name of the app you want to create and its authentication type. Feel free to use any name of your choice and either of the auth options.</p>
<p><img src="https://paper-attachments.dropbox.com/s_8728EF96CF25BE6F7A46E3619EB658CA92CDD4D1E377FEC5C8707FC59B5068A6_1658071816533_Screenshot+2022-07-17+at+16.29.23.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-step-3-get-your-robin-credentials">Step 3 – Get Your Robin Credentials</h1>
<p>Now that we have created an app on our Robin dashboard, there is something you should take note of. There are a couple of credentials you need when using Robin in your Nuxt app:</p>
<ul>
<li>API key,</li>
<li>User token,</li>
<li>User name,</li>
<li>Users, and</li>
<li>Keys</li>
</ul>
<p>Let’s go over each of them individually.</p>
<ul>
<li>API key: Robin automatically creates the API key when you create an app. You can retrieve it from either the getting started or the API config page on your dashboard. It is unique for every application.</li>
<li>User token: The user token is a unique identifier for every user of your Robin app. The token that should be passed to this property is yours since you’re the one using the app on your site. However, it is to be created by you, the user, typically on the server, and then used on the client side.</li>
<li>User name: The User name is the name of the current user of your Robin app. In this case, it will be your name. If you wanted someone else to include your Robin chat on their site or web app (i.e another user of your Robin app), it should be their name.</li>
<li>Users: Users is a list of the users on your Robin app. It usually contains their user tokens, profile images, and user names.</li>
<li>Keys: This fundamentally exists to help us be flexible in describing the user tokens, profile images, and user names in our users list. Here’s an example. If our keys object looks like this:</li>
</ul>
<pre><code class="lang-javascript">keys: {
  <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
  <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
  <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
}
</code></pre>
<p>Then our <code>users</code> array should describe the users’ tokens, profile images, and names with the values in the <code>keys</code> object.</p>
<p>Regardless of the users that would be using your Robin App, Robin requires a <code>userToken</code>, <code>profileImage</code> and a <code>userName</code> from them. Robin requires this for the display name and to identify each message sender and receiver uniquely on the platform.</p>
<pre><code class="lang-js">users: [
  {
    <span class="hljs-string">'user_token'</span>: <span class="hljs-string">'ABCDEF098765GH'</span>,
    <span class="hljs-string">'profile_image'</span>: <span class="hljs-string">'https://url-to-image'</span>,
    <span class="hljs-string">'user_name'</span>: <span class="hljs-string">'Article Reader'</span>
  }
]
</code></pre>
<h1 id="heading-step-4-install-robin-in-your-nuxt-app">Step 4 – Install Robin in Your Nuxt App</h1>
<p>Since we have everything we’ll need, we can go ahead and install Robin.</p>
<pre><code>npm i robin-vue
<span class="hljs-comment">// OR</span>
yarn add robin-vue
</code></pre><h1 id="heading-step-5-setup-the-robin-plugin">Step 5 – Setup the Robin Plugin</h1>
<p>In your <code>plugins</code> directory, create a <code>robin.js</code> file with the plugin setup:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> RobinChat <span class="hljs-keyword">from</span> <span class="hljs-string">'robin-vue'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'robin-vue/dist/style.css'</span>

Vue.use(RobinChat)
</code></pre>
<p>Note that we import the CSS because the <code>RobinChat</code> component does not include any CSS itself.</p>
<h1 id="heading-step-6-register-the-plugin">Step 6 – Register the Plugin</h1>
<p>The <code>plugins</code> property in the <code>nuxt.config.js</code> file is to let our Nuxt app know about the plugins that it should use. So if we don’t include our Robin plugin there, it won’t be available in our app.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-attr">plugins</span>: [
    { <span class="hljs-attr">src</span>: <span class="hljs-string">'~/plugins/robin.js'</span>, <span class="hljs-attr">mode</span>: <span class="hljs-string">'client'</span> }
  ]
}
</code></pre>
<h1 id="heading-step-7-use-the-plugin">Step 7 – Use the Plugin</h1>
<p>Now what’s left is for us to include the <code>RobinChat</code> component anywhere in our app and pass those credentials we discussed earlier as props. </p>
<p>Once again, the credentials are:</p>
<ul>
<li>API key,</li>
<li>User token,</li>
<li>User name,</li>
<li>Users, and</li>
<li>Keys</li>
</ul>
<p>In this list, what we currently don’t have is our user token and the tokens of the users on our app. </p>
<p>Recall that these tokens are usually created on the server. But we don’t have the luxury of that. So we can go ahead and create them with the help of <a target="_blank" href="https://www.npmjs.com/package/robin.io-js">Robin’s JavaScript SDK</a>. The Vue SDK we previously installed depends on this JavaScript SDK. So we don’t need to install it since it already exists in our app.</p>
<h2 id="heading-how-to-create-the-user-tokens">How to Create the User Tokens</h2>
<p>We can go ahead and create the tokens in the page we're going to include the chat UI. Because it’s for learning purposes, we can go ahead and create tokens for 5 users, ourselves included. We need to come up with usernames for each of them.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  &lt;!-- ... --&gt;
&lt;/template&gt;


<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">users</span>: [
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'idorenyin'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'ayo'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'elvis'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'favour'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'enoch'</span>
        }
      ],
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Note that the keys in every user object in the <code>users</code> array have to be defined in the <code>keys</code> object that we’ll be passing as a prop to the Robin component.</p>
<pre><code class="lang-javascript">keys: {
  <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
  <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
  <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
},
</code></pre>
<p>Next, we use the SDK’s <code>createUserToken()</code> function to create the tokens after creating a Robin instance, as it says in <a target="_blank" href="https://docs.robinapp.co/frontend-sdks/javascript/getting-started">Robin’s docs</a>.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  &lt;!-- ... --&gt;
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { Robin } <span class="hljs-keyword">from</span> <span class="hljs-string">'robin.io-js'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">keys</span>: {
        <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
        <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
        <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
      },
      <span class="hljs-attr">users</span>: [
        <span class="hljs-comment">// ...</span>
      ]
    }
  },
  created () {
    <span class="hljs-built_in">this</span>.createTokens()
  },
  <span class="hljs-attr">methods</span>: {
    <span class="hljs-keyword">async</span> createTokens () {
      <span class="hljs-keyword">const</span> robin = <span class="hljs-keyword">new</span> Robin(<span class="hljs-string">'API_KEY'</span>, <span class="hljs-literal">true</span>)
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">this</span>.users.length; i++) {
        <span class="hljs-keyword">await</span> robin.createUserToken({
          <span class="hljs-attr">meta_data</span>: {
            <span class="hljs-attr">username</span>: <span class="hljs-built_in">this</span>.users[i].user_name
          }
        }).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
          <span class="hljs-built_in">this</span>.users[i].user_token = res.data.user_token
        })
      }
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<h2 id="heading-how-to-use-credentials-on-the-robinchat-component">How to Use Credentials on the RobinChat Component</h2>
<p>We now have everything we need to display the Robin chat UI on our app. Whew!<br>We can now go ahead and use the tokens and the other credentials.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  &lt;!-- ... --&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RobinChat</span>
    <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tokensAreAvailable"</span>
    <span class="hljs-attr">:api-key</span>=<span class="hljs-string">"apiKey"</span>
    <span class="hljs-attr">:user-token</span>=<span class="hljs-string">"users[0].user_token"</span>
    <span class="hljs-attr">user-name</span>=<span class="hljs-string">"Idorenyin Udoh"</span>
    <span class="hljs-attr">:keys</span>=<span class="hljs-string">"keys"</span>
    <span class="hljs-attr">:users</span>=<span class="hljs-string">"users"</span>
  /&gt;</span></span>
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { Robin } <span class="hljs-keyword">from</span> <span class="hljs-string">'robin.io-js'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data () {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">tokensAreAvailable</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">apiKey</span>: <span class="hljs-string">'API_KEY'</span>,
      <span class="hljs-attr">keys</span>: {
        <span class="hljs-attr">userToken</span>: <span class="hljs-string">'user_token'</span>,
        <span class="hljs-attr">profileImage</span>: <span class="hljs-string">'profile_image'</span>,
        <span class="hljs-attr">userName</span>: <span class="hljs-string">'user_name'</span>
      },
      <span class="hljs-attr">users</span>: [
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'idorenyin'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'ayo'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'elvis'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'favour'</span>
        },
        {
          <span class="hljs-attr">user_token</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">profile_image</span>: <span class="hljs-string">''</span>,
          <span class="hljs-attr">user_name</span>: <span class="hljs-string">'enoch'</span>
        }
      ]
    }
  },
  created () {
    <span class="hljs-built_in">this</span>.createTokens()
  },
  <span class="hljs-attr">methods</span>: {
    <span class="hljs-keyword">async</span> createTokens () {
      <span class="hljs-keyword">const</span> robin = <span class="hljs-keyword">new</span> Robin(<span class="hljs-built_in">this</span>.apiKey, <span class="hljs-literal">true</span>)
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">this</span>.users.length; i++) {
        <span class="hljs-keyword">await</span> robin.createUserToken({
          <span class="hljs-attr">meta_data</span>: {
            <span class="hljs-attr">username</span>: <span class="hljs-built_in">this</span>.users[i].user_name
          }
        }).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
          <span class="hljs-built_in">this</span>.users[i].user_token = res.data.user_token
        })
      }
      <span class="hljs-built_in">this</span>.tokensAreAvailable = <span class="hljs-literal">true</span>
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Note that we only display the <code>RobinChat</code> component when all the users’ tokens are available to avoid errors.</p>
<p>This is what the result looks like:</p>
<p><img src="https://paper-attachments.dropbox.com/s_8728EF96CF25BE6F7A46E3619EB658CA92CDD4D1E377FEC5C8707FC59B5068A6_1658311851926_Screenshot+2022-07-20+at+11.10.45.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The app is available <a target="_blank" href="https://nuxt-chat-lmqlbq79p-idorenyinudoh.vercel.app/">here</a>.</p>
<p>Note that I used previously-created user tokens for this app because you wouldn’t be able to view messages if tokens are created every time the app loads. Permanent tokens are what make the messages on Robin persist.</p>
<p>Also, I created <a target="_blank" href="https://nuxt-chat-app-git-ayo-idorenyinudoh.vercel.app/">another app</a> for the user Ayo. You can check it out too. This way, you can test the real-time communication between Idorenyin and Ayo.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>You just learned how to implement real-time communication on a Nuxt application with Robin. </p>
<p>The ease of integration makes it super fast to implement a chat system in your app and focus on building/maintaining it. </p>
<p>If you make sure to create your users’ tokens on the server, then implementing the integration on the frontend wouldn't be too hard.</p>
<p>Happy building!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Flat-File Data in a Static Nuxt App ]]>
                </title>
                <description>
                    <![CDATA[ By Anthony Gore Making your Nuxt web app static can potentially save you the time and money of setting up a server-rendered app. It may also offer superior performance. But what if your app needs dynamic data? The most popular solution is to set up a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-flat-file-data-in-a-static-nuxt-app/</link>
                <guid isPermaLink="false">66d45d97d1ffc3d3eb89dda5</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 19 Jun 2020 18:33:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/flat_file_db.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Anthony Gore</p>
<p>Making your Nuxt web app static can potentially save you the time and money of setting up a server-rendered app. It may also offer superior performance.</p>
<p>But what if your app needs dynamic data? The most popular solution is to set up an API alongside your static app that can deliver dynamic data via AJAX.</p>
<p>In this article, I'll show you another possible architecture - using a flat-file database. This architecture might save you the hassle of setting up an API and offers superior performance.</p>
<h2 id="heading-what-is-a-flat-file-database">What is a flat-file database?</h2>
<p>A "flat-file database" is a database architecture where data is stored in a simple text file rather than in database software like MySQL or MongoDB.</p>
<p>In a Nuxt app, this file can be a JSON file that sits in your static files directory and is deployed alongside the markup files.</p>
<p>At runtime, the JSON file is loaded by the Nuxt app. Once the data is parsed as JavaScript data it can be used to power the app.</p>
<h2 id="heading-why-use-a-flat-file-database">Why use a flat-file database?</h2>
<p>Flat-file databases are advantageous because of their simplicity and low overhead. But they are also insecure and don't offer the performance benefits of conventional database software, which is why they're rarely used.</p>
<p>In the context of Nuxt apps, though, they have another great advantage – they can be stored and accessed from static hosting.</p>
<p>Using a flat-file database may also have a performance advantage over an API service which will have a small latency overhead incurred when requests are processed by the server.</p>
<p>However, flat-file DBs won't always be appropriate to use, as they offer no security and are read-only while in production. This means you'll need to rebuild the site any time you want to write new data.</p>
<p>A type of data that is a good candidate for flat-file storage and retrieval is meta data. For example, on the <a target="_blank" href="https://vuejsdevelopers.com/">Vue.js Developers blog</a>, which I built with Nuxt, I use a flat-file database to store meta data about published posts.</p>
<p>This allows me to easily access that data across the site, for example on the home page where the latest blog articles are displayed, and on the topics page which indexes posts based on topic tags applied (both shown below).</p>
<p><img src="https://s3.us-east-2.amazonaws.com/downloads.vuejsdevelopers.com/flat_file_db_image_1.png" alt="Vue.js Developers Blog" width="1618" height="613" loading="lazy"></p>
<h2 id="heading-implementing-the-flat-file-database-architecture-in-nuxt">Implementing the flat-file database architecture in Nuxt</h2>
<p>Now let's see how to implement the flat-file database architecture in your own Nuxt site.</p>
<p>Say we want to create a blog home page which show the latest published article like that on the Vue.js Developers blog.</p>
<p>We'll begin by seeing how flat-file-sourced data gets used in the page, and then work backwards until we can see how the whole architecture works.</p>
<h3 id="heading-using-flat-file-data-in-a-page">Using flat-file data in a page</h3>
<p>In our home page component, <em>pages/index.vue</em>, we'll import <code>getArticleSummaries</code> from a soon-to-be-created JavaScript module <code>flatFileDb</code>.</p>
<p>This method will return a Promise containing the article summary data ready for use on the page.</p>
<p>You can, of course, use this data at build time via <code>asyncData</code>, and at run time via the <code>created</code> hook.</p>
<p><em>pages/index.vue</em>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { getArticleSummaries } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/assets/js/flatFileDb"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">data</span>: <span class="hljs-function">() =&gt;</span> ({
        <span class="hljs-attr">articleSummaries</span>: []
    }),
    <span class="hljs-keyword">async</span> asyncData () {
        <span class="hljs-keyword">const</span> articleSummaries = <span class="hljs-keyword">await</span> getArticleSummaries();
        <span class="hljs-keyword">return</span> { articleSummaries }
    },
    <span class="hljs-keyword">async</span> created () {
        <span class="hljs-built_in">this</span>.articleSummaries = <span class="hljs-keyword">await</span> getArticleSummaries();
    }
}
</code></pre>
<p>Note that the data structure we'll get from <code>getArticleSummaries</code> will be an array of objects like this:</p>
<pre><code class="lang-js">[
    {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"..."</span>,
        <span class="hljs-attr">description</span>: <span class="hljs-string">"..."</span>,
        <span class="hljs-attr">published</span>: <span class="hljs-string">"..."</span>,
        ...
    },
    ...
]
</code></pre>
<p>Note: If you have multiple entities (for example, in addition to articles you also store information about videos), each will have its own flat file and its own retrieval method in the app, like <code>getVideoSummaries</code>.</p>
<h3 id="heading-flat-file-database-module">Flat-file database module</h3>
<p>We saw above that a <code>getArticleSummary</code> method was imported from the <code>flatFileDb</code> module. Let's see how we can implement that.</p>
<p>Our flat-file database will be included in our static files and should be a JSON file since these are simple to parse as valid JavaScript data.</p>
<p>We'll include this JSON file by using a dynamic import. This feature is designed for importing JavaScript modules, but it works with JSON files out-of-the-box with Webpack. Conveniently, you get the JSON file already parsed as JavaScript.</p>
<p>It's important to call the dynamic import in a <code>try/catch</code> block to prevent the app crashing if the file is missing or the JSON parsing fails.</p>
<p>Before we return the data to the consuming component we need to "decode" it with another custom method <code>decodeArticleSummaries</code>. I'll explain that in a moment.</p>
<p>Finally, note that a JSON file doesn't have a default export, so you'll need to access the <code>default</code> property of the db module to access the data.</p>
<p><em>assets/js/flatFileDb.js</em>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { decodeArticleSummaries } <span class="hljs-keyword">from</span> <span class="hljs-string">"dbDecoders"</span>;

<span class="hljs-keyword">const</span> getArticleSummaries = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> db = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">`@/static/article-summaries.json`</span>);
    <span class="hljs-keyword">return</span> decodeArticleSummaries(db.default);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
    <span class="hljs-keyword">return</span> [];
  }
};

<span class="hljs-keyword">export</span> { getArticleSummaries };
</code></pre>
<h3 id="heading-decoding-the-database">Decoding the database</h3>
<p>Above, I said the data provided to the component would look like this:</p>
<pre><code>{
    <span class="hljs-attr">title</span>: <span class="hljs-string">"..."</span>,
    <span class="hljs-attr">description</span>: <span class="hljs-string">"..."</span>,
    <span class="hljs-attr">published</span>: <span class="hljs-string">"..."</span>,
    <span class="hljs-comment">// etc</span>
}
</code></pre><p>However, it shouldn't be stored in the database like this because the property names are wastefully long.</p>
<p>In order to keep the flat file as lean as possible we should "encode" each key when the database is created. Then we should decode them before they're consumed by components so they have their full names available to the developer.</p>
<p>So, let's say we make "title" =&gt; "t", "description" =&gt; "d", and "published" =&gt; "p". In a large database, this transformation could reduce the file size by many bytes.</p>
<p><em>assets/js/dbDecode.js</em>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> decodeArticleSummaries = <span class="hljs-function"><span class="hljs-params">db</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> db.map(<span class="hljs-function"><span class="hljs-params">article</span> =&gt;</span> ({
        <span class="hljs-attr">title</span>: article.t,
        <span class="hljs-attr">description</span>: article.d,
        <span class="hljs-attr">published</span>: article.p
        <span class="hljs-comment">// etc</span>
    }));
}
</code></pre>
<h2 id="heading-generating-the-flat-file-database">Generating the flat-file database</h2>
<p>So now we've seen how the flat-file database is consumed at run time. How is it created?</p>
<p>You could create a flat-file database manually by hand, but usually you'll want to generate it at build time with a Node.js script.</p>
<p>In our example, we'll want to make a script that extracts the meta data of each article and stores it as <em>static/article-summaries.json</em>. Let's assume the articles are stored as markdown and are in an "articles" directory in the project root.</p>
<p>The details of the script will be specific to your implementation, so I'll just give you pseudo code to communicate the basic idea.</p>
<p><em>scripts/generateDb.js</em>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);
<span class="hljs-keyword">const</span> frontmatterExtractor = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./frontmatterExtractor"</span>);
<span class="hljs-keyword">const</span> encodeArticleSummaries = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./encodeArticleSummaries"</span>);

<span class="hljs-built_in">module</span>.exports = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// Load article files</span>
    <span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> fs.readdir(<span class="hljs-string">"/articles"</span>, <span class="hljs-function">(<span class="hljs-params">err, filePaths</span>) =&gt;</span> {
        <span class="hljs-comment">// Create the database by reading each file</span>
        <span class="hljs-keyword">const</span> db = filePaths.map(<span class="hljs-keyword">async</span> path =&gt; {
            <span class="hljs-keyword">const</span> file = <span class="hljs-keyword">await</span> fs.readFile(path);
            <span class="hljs-comment">// Extract the meta data</span>
            <span class="hljs-keyword">return</span> frontmatterExtractor(file);
        });
        <span class="hljs-comment">// Encode the data</span>
        <span class="hljs-keyword">const</span> encoded = encodeArticleSummaries(db);
        <span class="hljs-comment">// Write the database object to a JSON file</span>
        <span class="hljs-keyword">await</span> fs.writeFile(
            <span class="hljs-string">"/static/article-summaries.json"</span>, 
            <span class="hljs-built_in">JSON</span>.stringify(encoded)
        );
    });
}
</code></pre>
<h2 id="heading-running-the-db-generator-script-before-the-site-build">Running the DB generator script before the site build</h2>
<p>Now that we've got a database generator script, let's trigger it to run just before the build (or generate) processes which will want to consume it.</p>
<p>To do this, we'll squeeze it into the NPM commands in <em>package.json</em>. Note that by using the <code>&amp;&amp;</code> operator we can ensure the Nuxt process doesn't begin until the generator script completes.</p>
<p><em>package.json</em>:</p>
<pre><code class="lang-json">{
    ...
    <span class="hljs-attr">"scripts"</span>: {
        ...
        <span class="hljs-attr">"build"</span>: <span class="hljs-string">"node scripts/generateDb &amp;&amp; nuxt build"</span>,
        <span class="hljs-attr">"generate"</span>: <span class="hljs-string">"node scripts/generateDb &amp;&amp; nuxt generate"</span>,
        ...
    }
    ...
}
</code></pre>
<p>In development, however, I find it easier to manually generate the database on the command line whenever I need to update it:</p>
<pre><code>$ node scripts/generateDb
</code></pre><h2 id="heading-further-reading">Further reading</h2>
<p>That's the basic architecture explained. Here are a few other articles learn more:</p>
<ul>
<li><a target="_blank" href="https://blog.lichter.io/posts/jamstack-nuxt-netlify/">Going JAMstack with Netlify and Nuxt</a></li>
<li><a target="_blank" href="https://www.raymondcamden.com/2019/07/25/multiple-ways-of-api-integration-in-your-jamstack">Multiple Ways of API Integration in your JAMStack</a></li>
<li><a target="_blank" href="https://vuejsdevelopers.com/2018/12/31/vue-nuxt-spa-markdown/">Including Markdown Content in a Vue or Nuxt SPA</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Up & Going with Nuxt.js, Bulma and  Sass ]]>
                </title>
                <description>
                    <![CDATA[ By Eduardo Vedes TL;DR: Overcome Nuxt.js, Bulma and Sass shenanigans with this quick article to help you start developing your next App in less than 10 minutes. Hi everyone ❤️! Few days ago I found myself struggling a bit to put Nuxt.js, Bulma and Sa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/up-goind-with-nuxt-js-bulma-and-sass/</link>
                <guid isPermaLink="false">66d45f0b3a8352b6c5a2aa5b</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Bulma ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Sass ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 15 Jun 2019 11:09:27 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9ca202740569d1a4ca51f3.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Eduardo Vedes</p>
<p><strong>TL;DR: Overcome Nuxt.js, Bulma and Sass shenanigans with this quick article to help you start developing your next App in less than 10 minutes.</strong></p>
<p>Hi everyone ❤️! Few days ago I found myself struggling a bit to put <strong>Nuxt.js</strong>, <strong>Bulma</strong> and <strong>Sass</strong> to work correctly and the info I found on google didn't help too much. </p>
<p>Most of the configurations I found were not working, because they were outdated or didn't explain quite well how to do it. So I deep dived a little bit on this subject and decided to write an article to help you do the same in less than 10 minutes.</p>
<p>Let's have some fun and get our hands dirty while grokking a few concepts needed to do this.</p>
<h2 id="heading-1-scaffolding-nuxtjs">1. Scaffolding Nuxt.js</h2>
<p>Nowadays, to get started quickly with Nuxt.js we use a scaffolding tool called <strong><a target="_blank" href="https://github.com/nuxt/create-nuxt-app">create-nuxt-app</a></strong>. Please make sure you have <strong><a target="_blank" href="https://www.npmjs.com/package/npx">npx</a></strong> installed on your machine.</p>
<p>Let's open a terminal and do: <code>npx create-nuxt-app nuxt-bulma-sass</code>, where <code>nuxt-bulma-sass</code> is the name of the project we're scaffolding for the purpose of this article.</p>
<p><strong>create-nuxt-app</strong> will ask you some questions before creating the scaffold. For the purpose of this article I've chosen the following setup:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/bulma2.png" alt="Image" width="600" height="400" loading="lazy">
<em>create-nuxt-app init questions</em></p>
<p>So, the next step will be to change directory into our project folder:</p>
<p><code>cd nuxt-bulma-sass</code></p>
<p>and launch the project with: <code>yarn run dev</code>. (you can also use npm if you like it)</p>
<p>At this point we have our project running:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/bulma3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And if we open our browser on localhost:3000 we'll be getting this screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/Screen-Shot-2019-06-14-at-09.29.05.png" alt="Image" width="600" height="400" loading="lazy">
<em>localhost:3000 pages/index.vue</em></p>
<p>So at this point we have the pages/index.vue on the screen, which is the first page to be rendered in your project by default.</p>
<p>Let's replace the content of this file by the following one:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/carbon1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If we inspect our page in the browser we see we got <strong>bulma</strong> installed because section is formatted according to it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/Screen-Shot-2019-06-14-at-09.45.03.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Easy peasy lemon squeezy.</p>
<p>Let's add a class and choose some colors:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/carbon-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/Screen-Shot-2019-06-14-at-09.47.55.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>What if we want to nest .<em>hello-nuxt</em> inside .<em>edo-theme</em>? We're going to need SASS to be able to do it.</p>
<h2 id="heading-2-adding-sass">2. Adding Sass</h2>
<p>So, to add Sass to our project we'll need to stop our running app (Ctrl+c) and do the following:</p>
<p><code>yarn add node-sass sass-loader --dev</code></p>
<p>These are the two packages needed as dev-dependencies to be able to have Sass in our boilerplate.</p>
<p>Note that we're adding it as a dev dependency because we only need it while developing and at build time. After that <strong>Sass</strong> is transformed into <strong>CSS</strong> and we don't need it anymore.</p>
<p>Let's sneak peek my package.json for you to check it:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/Screen-Shot-2019-06-14-at-09.57.38.png" alt="Image" width="600" height="400" loading="lazy">
<em>package.json with sass added to the project</em></p>
<p>Okay everyone ❤️, at this point we're able to nest the classes we wanted to.</p>
<p>Let's run our boilerplate again: <code>yarn run dev</code> and do the tweaks needed ?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/carbon--1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/Screen-Shot-2019-06-14-at-10.05.23.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Noice! We already did a lot today! Go grab a coffee ☕, I'll wait for you here ?</p>
<p>Okay, let's abstract things a bit and create this file <em>~/assets/scss/main.scss</em> and put there some classes and variables:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/carbon-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>new ~/assets/scss/main.scss</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/carbon--1--1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Nice! It's working!</p>
<p>Now we have two problems: </p>
<ol>
<li>We need to import main.scss into each one of our pages/components, which is not nice. We want to import it only once and have it available in all our </li></ol> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to generate a static website with Vue.js in no time ]]>
                </title>
                <description>
                    <![CDATA[ By Ondřej Polesný You have decided to build a static site, but where do you start? How do you select the right tool for the job without previous experience? How can you ensure that you succeed the first time, while avoiding tools that won’t help you ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-generate-a-static-website-with-vue-js-in-no-time-e74e7073b7b8/</link>
                <guid isPermaLink="false">66c351febbe01f981047867f</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 18 Dec 2018 17:33:58 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*LLjrzVGPTIotAeil7DiXyA@2x.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ondřej Polesný</p>
<p>You have decided to build a static site, but where do you start? How do you select the right tool for the job without previous experience? How can you ensure that you succeed the first time, while avoiding tools that won’t help you in the end?</p>
<p>In this article, you will learn how to adjust a Vue.js website to be automatically generated as a static site.</p>
<h3 id="heading-introduction">Introduction</h3>
<p>I summarized the key differences between an API based website and static sites in my <a target="_blank" href="http://bit.ly/2QVSm9a">previous article</a>. As a quick reminder, static sites are:</p>
<ul>
<li>Blazing fast</li>
<li>Secure (as they are just a set of static pages)</li>
<li>Regenerated every time editors update the content</li>
<li>Compatible with additional dynamic functionality</li>
</ul>
<h4 id="heading-what-is-a-static-site-generator">What is a Static Site Generator?</h4>
<p>A static site generator is a tool that generates a static website from a website’s implementation and content.</p>
<p>Content can come from a headless content management system, through a REST API. The website implementation uses one of the JavaScript frameworks like Vue.js or React. The output of a static site generator is a set of static files that form the website.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0VwMHb8tn3Cn8aNcazzqntftAKyRki4svRCc" alt="Image" width="800" height="265" loading="lazy"></p>
<h4 id="heading-static-site-implementation">Static Site Implementation</h4>
<p>I chose Vue.js as the JavaScript framework to use. Therefore I will be working with <a target="_blank" href="http://bit.ly/2Aiaggm">Nuxt.js</a>, which is a static site generator for Vue.js.</p>
<p>If you are using a different framework, look for a static site generator built on top of that framework (for example <a target="_blank" href="http://bit.ly/2ypBwZ7">Gatsby</a> for <a target="_blank" href="http://bit.ly/2PGeCTL">React.js</a>).</p>
<p>Essentially, Nuxt is a combination of multiple tools that together enable you to create static sites. The tools include:</p>
<ul>
<li>Vue2 — Core Vue.js library.</li>
<li>Vue Router — Handles URL routing for pages within the website.</li>
<li>Vuex — Memory store for data that are shared by components.</li>
<li>Vue Server Renderer — Enables server side rendering of pages before the actual static files generation</li>
<li>Vue-Meta — Manages page metadata info</li>
</ul>
<p>Nuxt also defines how the website needs to be built in order to generate static files.</p>
<h4 id="heading-installation">Installation</h4>
<p>In order to start building websites with Nuxt, you need to install it. See detailed installation instructions on the <a target="_blank" href="http://bit.ly/2R0LTJH">Nuxt.js webpage</a>. In a nutshell, you need <code>npx</code> (shipped with NPM by default) installed and run:</p>
<pre><code>npx create-nuxt-app &lt;website-name&gt;
</code></pre><p>You can just use default selections, unless you have preferences otherwise.</p>
<h4 id="heading-components">Components</h4>
<p>In <a target="_blank" href="http://bit.ly/2zLRE8a">one of my previous articles</a> I explained how to create a template layout and components. All of them were defined within single file <code>components.js</code>. That needs to be changed with Nuxt. All components need to be extracted from <code>components.js</code> file into separate files under folder <code>components</code>. Take a look at my <code>blog-list</code> component and its previous implementation:</p>
<pre><code>Vue.component(<span class="hljs-string">'blog-list'</span>, { <span class="hljs-attr">props</span>: [<span class="hljs-string">'limit'</span>], <span class="hljs-attr">data</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{  <span class="hljs-keyword">return</span> {   <span class="hljs-attr">articles</span>: <span class="hljs-literal">null</span>  } },
</code></pre><pre><code> created: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{  <span class="hljs-keyword">var</span> query = deliveryClient   .items()   .type(<span class="hljs-string">'blog_post'</span>)   .elementsParameter([<span class="hljs-string">'link'</span>, <span class="hljs-string">'title'</span>, <span class="hljs-string">'image_url'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'teaser'</span>])   .orderParameter(<span class="hljs-string">'elements.published'</span>, SortOrder.desc);   <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.limit){   query = query.limitParameter(<span class="hljs-built_in">this</span>.limit);  }  query   .getPromise()   .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span>    <span class="hljs-built_in">this</span>.$data.articles = response.items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> ({     <span class="hljs-attr">url</span>: item.link.value,     <span class="hljs-attr">header</span>: item.title.value,     <span class="hljs-attr">image</span>: item.image_url.value != <span class="hljs-string">''</span> ? item.image_url.value : item.image.assets[<span class="hljs-number">0</span>].url,     <span class="hljs-attr">teaser</span>: item.teaser.value    }))   ); },
</code></pre><pre><code> template: <span class="hljs-string">`  &lt;section class="features"&gt;   &lt;article v-for="article in articles"&gt;    ...   &lt;/article&gt;  &lt;/section&gt; `</span> });
</code></pre><p>To separate it, you also need to change the component’s syntax to match the following template:</p>
<pre><code>&lt;template&gt; HTML <span class="hljs-keyword">of</span> the component&lt;<span class="hljs-regexp">/template&gt;&lt;script&gt; export default {  Vue.js code }&lt;/</span>script&gt;
</code></pre><p>Therefore my adjusted <code>Blog-list</code> component looks like this:</p>
<pre><code>&lt;template&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"features"</span>&gt;</span>  <span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"article in blogPosts"</span>&gt;</span>   ...  <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>&lt;<span class="hljs-regexp">/template&gt;&lt;script&gt; export default {  props: ['limit'],  computed: {   blogPosts: function(){    return this.$store.state.blogPosts &amp;&amp; this.limit &amp;&amp; this.$store.state.blogPosts.length &gt; this.limit ? this.$store.state.blogPosts.slice(0, this.limit) : this.$store.state.blogPosts;   }  } }&lt;/</span>script&gt;
</code></pre><p>You see the template stayed the same. What changed is the implementation that is now within <code>export default</code> section. Also, there used to be a function gathering data from headless CMS Kentico Cloud.</p>
<p>That content is now stored within Vuex store. I will explain this part later. Convert all of your components this way, to contain template within <code>&lt;templa</code>te&gt; tags and implementation w<code>ithin &amp;l</code>t;script&gt; tags. You should end up with a similar file structure as I have:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/cZ4HD2jJIJ0bTeTi5ZGwFYRTMYr2p6L0HQzL" alt="Image" width="800" height="316" loading="lazy"></p>
<p>Note that I have two new components here — Menu and Header. As my aim was to also improve performance, I decided to remove jQuery from my website. jQuery is quite a large and heavy library that was used only for small UI effects. I was able to recreate them using just Vue.js. Therefore, I converted the static HTML representing header to component. I also added the UI related functionality into <code>mounted</code> function of this component.</p>
<pre><code>mounted: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-built_in">window</span>.addEventListener(‘scroll’, <span class="hljs-built_in">this</span>.scroll); …},<span class="hljs-attr">methods</span>: { <span class="hljs-attr">scroll</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{  … }}
</code></pre><h3 id="heading-handling-vuejs-events-with-nuxt">Handling Vue.js Events with Nuxt</h3>
<p>I used to leverage Vue events in my website. The main reason was reCaptcha control used for form validation. When it was initialized, it would broadcast the event so that form component can unlock the submit button of the contact form.</p>
<p>With Nuxt, I no longer use <code>app.js</code> or <code>components.js</code> files. Therefore I created a new <code>recaptcha.js</code> file that contains a simple function emitting the event when reCaptcha gets ready. Note that instead of creating new Vue.js instance just for events (<code>let bus = new Vue();</code> in my old code), it is possible to simply use <code>this.$nuxt</code> the same way.</p>
<pre><code><span class="hljs-keyword">var</span> recaptchaLoaded = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-built_in">this</span>.$nuxt.$emit(<span class="hljs-string">'recaptchaLoaded'</span>);}
</code></pre><h3 id="heading-layout-and-pages">Layout and Pages</h3>
<p>The main frame of the page was <code>index.html</code>, and each page defined its own layout in constants that were handed over to Vue router.</p>
<p>With Nuxt, the main frame including <code>&lt;ht</code>ml&gt; <code>tag</code>, meta tags and other essentials of any HTML page are handled by Nuxt. The actual website implementation is handling only content w<code>ithin</code>  tags. Move the layout that is common for all your <code>pages into layouts</code>/default.vue and respect the same template as with components. My layout looks like this:</p>
<pre><code>&lt;template&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  <span class="hljs-tag">&lt;<span class="hljs-name">Menu</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Menu</span>&gt;</span>  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"page-wrapper"</span>&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">Header</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Header</span>&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">nuxt</span>/&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"footer"</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inner"</span>&gt;</span>     …     <span class="hljs-tag">&lt;<span class="hljs-name">ContactForm</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ContactForm</span>&gt;</span>     …    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>   <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>&lt;<span class="hljs-regexp">/template&gt;&lt;script&gt; import ContactForm from ‘~/</span>components/Contact-form.vue’ <span class="hljs-keyword">import</span> Menu <span class="hljs-keyword">from</span> ‘~<span class="hljs-regexp">/components/</span>Menu.vue’ <span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> ‘~<span class="hljs-regexp">/components/</span>Header.vue’  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {  <span class="hljs-attr">components</span>: {   ContactForm,   Menu,   Header  } } &lt;/script&gt;
</code></pre><p>The layout is basically the HTML markup of my old <code>index.html</code>. However, note the <code>&lt;scri</code>pt&gt; section. All of the components I want to use within this layout template need to be imported from their location and specified in exported object.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/FwQ-P1izN6Qai74S7TrMPcJGJ2SOL-7K93X-" alt="Image" width="639" height="221" loading="lazy"></p>
<p>Page components were previously defined in <code>app.js</code> as constants. Take a look at my old Home page for example:</p>
<pre><code><span class="hljs-keyword">const</span> Home = { <span class="hljs-attr">template</span>: <span class="hljs-string">`  &lt;div&gt;   &lt;banner&gt;&lt;/banner&gt;   &lt;section id="wrapper"&gt;    &lt;about-overview&gt;&lt;/about-overview&gt;    ...    &lt;blog-list limit="4"&gt;&lt;/blog-list&gt;    &lt;ul class="actions"&gt;     &lt;li&gt;&lt;a href="/blog" class="button"&gt;See all&lt;/a&gt;&lt;/li&gt;    &lt;/ul&gt;    ...   &lt;/section&gt;  &lt;/div&gt; `</span>}
</code></pre><p>All these pages need to be defined in separate files within <code>pages</code> folder. Main page is always called <code>index.vue</code>. This is how my new <code>pages/index.vue</code> (my Homepage) looks like:</p>
<pre><code>&lt;template&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  <span class="hljs-tag">&lt;<span class="hljs-name">Banner</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Banner</span>&gt;</span>  <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">AboutOverview</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">AboutOverview</span>&gt;</span>   ...   <span class="hljs-tag">&lt;<span class="hljs-name">BlogList</span> <span class="hljs-attr">limit</span>=<span class="hljs-string">"4"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">BlogList</span>&gt;</span>   <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"actions"</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/blog"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button"</span>&gt;</span>See all<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>   <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>   ...  <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>&lt;<span class="hljs-regexp">/template&gt;&lt;script&gt; import Banner from ‘~/</span>components/Banner.vue’ <span class="hljs-keyword">import</span> AboutOverview <span class="hljs-keyword">from</span> ‘~<span class="hljs-regexp">/components/</span>About-overview.vue’ <span class="hljs-keyword">import</span> BlogList <span class="hljs-keyword">from</span> ‘~<span class="hljs-regexp">/components/</span>Blog-list.vue’  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {  <span class="hljs-attr">components</span>: {   Banner,   AboutOverview,   BlogList  }, }&lt;/script&gt;
</code></pre><h3 id="heading-where-to-store-assets">Where to Store Assets</h3>
<p>On every website there are assets like CSS stylesheets, images, logos, and JavaScripts. With Nuxt, all these static files need to be stored under folder static. So the folder structure currently looks like this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/pez1-XpfChKIX8aOVP0vP-2BH2cz28MCsFlM" alt="Image" width="800" height="514" loading="lazy"></p>
<p>When you reference any resources from CSS stylesheets like fonts or images, you need to use static folder as a root:</p>
<pre><code>background-image: url(<span class="hljs-string">"~/assets/images/bg.jpg"</span>);
</code></pre><h3 id="heading-getting-content">Getting Content</h3>
<p>With all the components and pages in place, we finally get to it: getting content into components.</p>
<p>Getting content using Nuxt is a bit different than it used to be. The important aspect of this process when using a static site generator is that the content needs to be gathered before all the pages are generated. Otherwise you will end up with a static website, but requests for content would still be dynamic, hitting the headless CMS from each visitor’s browser and you would lose the main benefit.</p>
<p>Nuxt contains two special methods that handle asynchronous data fetching at the right time, that is before the pages are generated. These methods are <code>asyncData</code> and <code>fetch</code>. They are available only to page components (that is files within <code>pages</code> folder) and their purpose is the same, but <code>asyncData</code> will automatically store received data within the component dataset.</p>
<p>This can be beneficial if you have many components on a single page using the same set of data. In my case, I even have multiple pages with many components that need to share the same data. Therefore I would either need to request the same data on each page or use a shared space that all pages and components could access.</p>
<p>I chose the latter. Nuxt makes it very easy for us. It automatically includes Vuex store that enables our components and pages access any data that are within the store. To start using the store all you need to do is create an <code>index.js</code> file within the <code>store</code> folder.</p>
<pre><code><span class="hljs-keyword">import</span> Vuex <span class="hljs-keyword">from</span> <span class="hljs-string">'vuex'</span>
</code></pre><pre><code><span class="hljs-keyword">const</span> createStore = <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Vuex.Store({  <span class="hljs-attr">state</span>: <span class="hljs-function">() =&gt;</span> ({}),  <span class="hljs-attr">mutations</span>: {},  <span class="hljs-attr">actions</span>: {}, })}<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createStore
</code></pre><p>You see the instance has a few properties:</p>
<ul>
<li><strong>State</strong><br>State is similar to data in components. It contains definition of data fields that are used to store data.</li>
<li><strong>Mutations</strong><br>Mutation are special functions that are permitted to change data in State (mutate the state).</li>
<li><strong>Actions</strong><br>Actions are simple methods that enable you to, for example, implement content gathering logic.</li>
</ul>
<p>Let’s get back to the <code>Blog-list</code> component. This component needs an array of blog posts in order to render its markup. Therefore blog posts need to be stored within Vuex store:</p>
<pre><code>…<span class="hljs-keyword">const</span> createStore = <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Vuex.Store({  <span class="hljs-attr">state</span>: <span class="hljs-function">() =&gt;</span> ({   <span class="hljs-attr">blogPosts</span>: <span class="hljs-literal">null</span>  }),  <span class="hljs-attr">mutations</span>: {   setBlogPosts(state, blogPosts){    state.blogPosts = blogPosts;   }  },  <span class="hljs-attr">actions</span>: {   getBlogPosts (context) {    logic to get content <span class="hljs-keyword">from</span> Kentico Cloud   }  }, })}
</code></pre><p>After adjusting Vuex store this way, the <code>Blog-list</code> component can use its data:</p>
<pre><code>&lt;article v-<span class="hljs-keyword">for</span>=<span class="hljs-string">"article in $store.state.blogPosts"</span>&gt; …&lt;/article&gt;
</code></pre><p>I already shared the whole implementation of this component above. If you noticed, it uses <code>computed</code> function as a middle layer between component markup and Vuex store. That middle layer ensures the component displays only a specific amount of articles as configured in the <code>limit</code> field.</p>
<h3 id="heading-querying-the-headless-cms">Querying the Headless CMS</h3>
<p>Maybe you remember the <code>deliveryClient</code> was used to get data from <a target="_blank" href="http://bit.ly/2QzUALM">Kentico Cloud</a> into the components.</p>
<p><em>Disclaimer: I work for Kentico, a CMS vendor that provides both traditional (coupled) CMS and a new cloud-first headless CMS — Kentico Cloud.</em></p>
<p>The very same logic can be used here in the Vuex store actions with a little tweak. Kentico Cloud has a <a target="_blank" href="http://bit.ly/2Qiovur">module for Nuxt.js</a>, install it using following command:</p>
<pre><code>npm i kenticocloud-nuxt-<span class="hljs-built_in">module</span> — savenpm i rxjs — save
</code></pre><p>With this module you can keep using <code>deliveryClient</code> like before, just with a <code>$</code> prefix. So in my case the logic to get blog posts looks like this:</p>
<pre><code>…getBlogPosts (context) { <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.$deliveryClient  .items()  ...  .orderParameter(<span class="hljs-string">'elements.published'</span>, SortOrder.desc)  .getPromise()  .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {   context.commit(<span class="hljs-string">'setBlogPosts'</span>, response.items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> ({    <span class="hljs-attr">url</span>: item.link.value,    <span class="hljs-attr">header</span>: item.title.value,    <span class="hljs-attr">image</span>: item.image_url.value != <span class="hljs-string">''</span> ? item.image_url.value : item.image.assets[<span class="hljs-number">0</span>].url,    <span class="hljs-attr">teaser</span>: item.teaser.value   })))  }); },…
</code></pre><p>If you want to use ordering using the <code>orderParameter</code>, you may need to include another import in the <code>store/index.js</code> file:</p>
<pre><code><span class="hljs-keyword">import</span> { SortOrder } <span class="hljs-keyword">from</span> <span class="hljs-string">'kentico-cloud-delivery'</span>
</code></pre><p>Now when the content gathering logic is implemented, it’s time to use the special asynchronous function fetch that I mentioned before. See my implementation in the <code>index.vue</code> page:</p>
<pre><code><span class="hljs-keyword">async</span> fetch ({store, params}) { <span class="hljs-keyword">await</span> store.dispatch(<span class="hljs-string">'getBlogPosts'</span>)}
</code></pre><p>The call to <code>store.dispatch</code> automatically invokes <code>getBlogPosts</code> action. Within the <code>getBlogPosts</code> implementation note the call for <code>context.commit</code>. <code>context</code> refers to Vuex store and <code>commit</code> will hand over blog posts data to <code>setBlogPosts</code> mutation. Updating the data set with blog posts changes the state of the store (mutates it). And we are almost finished!</p>
<h4 id="heading-other-content-storage-options">Other content storage options</h4>
<p>I used <a target="_blank" href="http://bit.ly/2QzUALM">Kentico Cloud</a> headless CMS and its API here, as I am using it throughout all articles in this series. If you want to also check out other options, you can find an independent list of headless CMSs and their features at <a target="_blank" href="http://bit.ly/2S8gxSi">headlesscms.org</a>.</p>
<p>If you don’t want to use a headless CMS and its API, you may decide to store your content in some other way — usually as a set of markdown files directly stored within your project or Git repository. You can find a nice example of this approach in <a target="_blank" href="http://bit.ly/2R5PQAo">nuxt-markdown-example GitHub repository</a>.</p>
<h3 id="heading-nuxt-configuration">Nuxt Configuration</h3>
<p>The whole application needs to be properly configured using <code>Nuxt.config.js</code> file. This file contains information about used modules, their configuration and site essentials like title or SEO tags. The configuration of my website looks like this:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { <span class="hljs-attr">head</span>: {  <span class="hljs-attr">title</span>: <span class="hljs-string">'Ondrej Polesny'</span>,  <span class="hljs-attr">meta</span>: [   { <span class="hljs-attr">charset</span>: <span class="hljs-string">'utf-8'</span> },   ...   { <span class="hljs-attr">hid</span>: <span class="hljs-string">'description'</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'description'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Ondrej Polesny — Developer Evangelist + dog lover + freelance bus driver'</span> }  ],  <span class="hljs-attr">script</span>: [   { <span class="hljs-attr">src</span>: <span class="hljs-string">'https://www.google.com/recaptcha/api.js?onload=recaptchaLoaded'</span>, <span class="hljs-attr">type</span>: <span class="hljs-string">"text/javascript"</span> },   { <span class="hljs-attr">src</span>: <span class="hljs-string">'assets/js/recaptcha.js'</span>, <span class="hljs-attr">type</span>: <span class="hljs-string">"text/javascript"</span> }  ], }, <span class="hljs-attr">modules</span>: [  <span class="hljs-string">'kenticocloud-nuxt-module'</span> ], <span class="hljs-attr">kenticocloud</span>: {  <span class="hljs-attr">projectId</span>: <span class="hljs-string">'*KenticoCloud projectId*'</span>,  <span class="hljs-attr">enableAdvancedLogging</span>: <span class="hljs-literal">false</span>,  <span class="hljs-attr">previewApiKey</span>: <span class="hljs-string">''</span> }, <span class="hljs-attr">css</span>: [  {<span class="hljs-attr">src</span>: <span class="hljs-string">'static/assets/css/main.css'</span>}, ], <span class="hljs-attr">build</span>: {  <span class="hljs-attr">extractCSS</span>: {   <span class="hljs-attr">allChunks</span>: <span class="hljs-literal">true</span>  } }}
</code></pre><p>The head section describes website essentials like <code>title</code> and <code>meta</code> tags you want to include in header.</p>
<p>Note the <code>modules</code> and <code>kenticocloud</code> configuration. The first one lists all modules your application depends on and the second one is specific module configuration. This is the place where you need to put your project API key.</p>
<p>To see all the options for meta tags, please refer to [https://github.com/declandewet/vue-meta](http://  https://github.com/declandewet/vue-meta)</p>
<h3 id="heading-running-and-generating">Running and Generating</h3>
<p>Static sites need to be generated before anyone can access them or upload them to an FTP server. However, it would be very time consuming to regenerate the site every single time a change has been made during the development phase. Therefore, you can run the application locally using:</p>
<pre><code>npm run dev
</code></pre><p>This will create a development server for you and enable you to access your website on http://localhost:8000 (or similar). While you keep your console running this command, every change you make in your scripts will have immediate effect on the website.</p>
<p>To generate a true static site, execute following command:</p>
<pre><code>npx nuxt generate
</code></pre><p>The output, that is your static site, will be in <code>dist</code> folder. Feel free to open any page in your favorite text editor and see if the source code contains content from the headless CMS. If it does, it was successfully fetched.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Having a generated static site will greatly improve the website’s performance. Compared to traditional sites, the webserver does not need to perform any CPU heavy operations. It only serves static files.</p>
<p>Compared to API based websites, the clients receive requested data instantly with the very first response. That’s what makes them all that fast — they do not need to wait for external content to be delivered via additional asynchronous requests. The content is already there in the first response from the server. That dramatically improves user experience.</p>
<p>Converting the site from Vue.js implementation to Nuxt definitions is very straightforward and does not require deep knowledge of all used components and packages.</p>
<p>Have you successfully created your first static site? Have you experienced any struggles? Please leave a comment.</p>
<p>In the next article I will focus on automated regeneration of static sites and where to host them, so stay tuned.</p>
<h4 id="heading-other-articles-in-the-series">Other articles in the series:</h4>
<ol>
<li><a target="_blank" href="http://bit.ly/2Duglu1">How to start creating an impressive website for the first time</a></li>
<li><a target="_blank" href="http://bit.ly/2N0kXY4">How to decide on the best technology for your website?</a></li>
<li><a target="_blank" href="http://bit.ly/2zLRE8a">How to power up your website with Vue.js and minimal effort</a></li>
<li><a target="_blank" href="http://bit.ly/2CyDnhX">How to Mix Headless CMS with a Vue.js Website and Pay Zero</a></li>
<li><a target="_blank" href="http://bit.ly/2P0gidP">How to Make Form Submissions Secure on an API Website</a></li>
<li><a target="_blank" href="http://bit.ly/2QVSm9a">Building a super-fast and secure website with a CMS is no big deal. Or is it?</a></li>
<li><strong>How to generate a static website with Vue.js in no time</strong></li>
<li><a target="_blank" href="http://bit.ly/2Dv2UGS">How to quickly set up a build process for a static site</a></li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
