Seamless Fullstack Nuxt Apps with Nuxt Hub

NuxtHub is a deployment and admin platform making it possible to build and deploy fullstack Nuxt application with ease. In this article we will explore this amazing new tool and what capabilities it offers to the ecosystem.

Charles Allotey
Nuxt 3

The Mastering Nuxt FullStack Unleashed Course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view course

Nuxt, the framework built on top of Vue.js, has long been a favorite among web developers for creating high-performance Server Side Rendered (SSR) web applications.

With the introduction of NuxtHub (currently in beta release), a powerful deployment and administration platform built on Cloudflare's infrastructure, Nuxt is poised to reach new heights.

NuxtHub

Nuxt Hub is designed to provide a streamlined, efficient experience for Nuxt.js developers, making it easier than ever to build, deploy and manage full-stack Nuxt applications.

In this article, we will delve into this game-changing tool and explore its various capabilities through hands-on experience.

One of NuxtHub's standout features is its "zero-configuration" deployment. This means that developers can deploy their applications without worrying about the intricate setup processes that typically accompany deployment. By leveraging Cloudflare's extensive network, NuxtHub ensures that your application is up and running with minimal effort, allowing you to focus on what truly matters: your application's core functionality.

Additionally, NuxtHub isn't just about deployment; it's also about empowering developers with a comprehensive suite of tools designed for building full-stack Nuxt applications. Here’s what you can expect from NuxtHub:

  • **Built-In SQL Database:** With hubDatabase(), you can store and manage your application's essential data securely and efficiently.
  • **Built-in Key-Value Storage:** Use hubKV() for globally accessible JSON data, perfect for configurations and user preferences that require low latency.
  • **Built-in Blob Storage:** hubBlob() offers a solution for uploading and storing static assets like images and videos and other unstructured data into your Nuxt application.
  • **Cache Storage:** By seamlessly integrating with Nitro's caching mechanisms, cachedEventHandler, and cachedFunction, NuxtHub optimizes server responses for faster loading times using Cloudflare Workers KV.
  • and more!

NuxtHub also offers a command-line interface (CLI) which simplifies integration with your current Nuxt development workflow. Whether you need to link your local project to a NuxtHub project or create a new one, the CLI makes these tasks straightforward. It ensures that your project is built with the correct settings and facilitates deployment to your Cloudflare account, complete with all necessary resources pre-configured.

NuxtHub offers several compelling benefits that make it an invaluable tool for you as Nuxt developers. And let’s not forget, it’s still Nuxt so we are definitely going to get some Nuxt’s awesomeness in there.

NuxtHub in Action

Let’s explore some capabilities of NuxtHub by building a simple Todo application. We’ll leverage NuxtHub’s built-in SQL Database support (hubDatabase()) to store our entries.

First, let's create our NuxtHub project.

npx nuxthub init my-todo-app

cd my-todo-app

npm run dev

Our Nuxt project with NuxtHub preinstalled is now up and running🚀.

NuxtHub

You can alternatively add it to an existing Nuxt project via:

npx nuxi@latest module add hub

and then Install wrangler development dependency to your project:

npm install --save-dev wrangler

That's it! You can now use NuxtHub features in your Nuxt project.

Let’s jump into our app.

We can proceed to exploring our Nuxt config file (nuxt.config.js):

NuxtHub

Notice that NuxtHub is added to our installed modules and, in a wider observation, our Nuxt project is also running on the Nuxt 4 directory structure (optional). Awesome 🤯

Additionally, we have activated all core features of NuxtHub, including kv, blob, cache, and Database. For this tutorial, I will focus on the Database feature, using hubDatabase() to create our to-do application.

To begin, we can create our UI components

// pages/index.vue

<template>
  <main class="w-full h-screen flex items-center justify-center">
    <UCard class="w-96">
      <template #header>
        <h3 class="text-lg font-semibold leading-6">Todo List</h3>
      </template>
      <div class="flex items-center gap-2">
        <UInput
          ref="newTodoInput"
          name="todo"
          class="flex-1"
          placeholder="Add a Todo.."
          autocomplete="off"
          autofocus
          :ui="{ wrapper: 'flex-1' }"
        />

        <UButton type="submit" icon="i-heroicons-plus-20-solid" />
      </div>

      <ul class="divide-y divide-gray-200 dark:divide-gray-800 mt-10">
        <li class="flex items-center gap-4 py-2">
          <span
            class="flex-1 font-medium"
            >Example Todo</span
          >

          <UButton
            color="red"
            variant="soft"
            size="2xs"
            icon="i-heroicons-x-mark-20-solid"
          />
        </li>
      </ul>
    </UCard>
  </main>
</template>

NuxtHub

Our UI is complete leveraging Nuxt UI components.

Now let’s jump into our server to create our API routes.

We will first create a folder named todos to handle all our todo server API routes.

We will first create a get route to fetch all todos

// server/api/todos/index.get.js

export default eventHandler(async (event) => {
  const db = hubDatabase()

  await db.exec('CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY, name TEXT, completed INTEGER, created_at INTEGER)')

  // List todos
  const { results: todos } = await db.prepare('SELECT * FROM todos ORDER BY created_at DESC').all()

  return todos
})

NuxtHub offers a server composable hubDatabase() that returns a D1 database client to connect to our database.

We then create our todos tables if it does not exist and return our todos list.

Now let’s go further to create our post server route to add new to-do entries to our db.

// server/api/todos/index.post.js

export default eventHandler(async (event) => {
    const { todo } = await readBody(event)
    const db = hubDatabase()
  
    await db
      .prepare('INSERT INTO todos (name, completed, created_at) VALUES (?1, ?2, ?3)')
      .bind(todo.name, todo.completed, Date.now())
      .run()
  
    return {}
  })

We can now post new todos in our todo db.

We are offered some helper methods to aid in querying and running some database actions.

prepare()

Generates a prepared statement to be used later.

bind()

Binds parameters to a prepared statement.

all()

Returns all rows as an array of objects, with each result row represented as an object on the results property.

first()

Returns the first row of the results. This does not return metadata like the other methods. Instead, it returns the object directly.

and more. It is recommended reading the full Cloudflare D1 documentation to understand the full potential of the D1 database.

Let’s move on to our the next feature which is deleting an item from our todos list.

Setting up our delete server api route:

// server/api/todos/[id].get.js

export default eventHandler(async (event) => {
    // Get the ID from the event (assuming it's passed as an argument)
    const { id } = event.context.params
  
    if (!id) {
      throw new Error('Missing ID to delete todo');
    }
  
    const db = hubDatabase();
  
    // Delete the todo with the specific ID
    await db.prepare('DELETE FROM todos WHERE id = ?').bind(id).run();
  
    // No need to return anything as the todo is deleted
  
  });

Let’s now connect our Server to our Client

// pages/index.vue

<script setup>
 const { data: todos, refresh } = await useFetch('/api/todos')

const newTodo = ref('')

async function submitTodo (){
  if (!newTodo.value.trim()) return
  await $fetch('/api/todos', {
    method: 'POST',
    body: {
      todo:{
        name: newTodo.value,
        completed: 0
      }

    }
  })
  newTodo.value = ''
  await refresh()
}

async function deleTodo (id){
  await $fetch(`/api/todos/${id}`, {
    method: 'DELETE'
  })
  await refresh()
}
</script>

<template>
  <main class="w-full h-screen flex items-center justify-center">
    <UCard class="w-96" @submit.prevent="submitTodo">
      <template #header>
        <h3 class="text-lg font-semibold leading-6">Todo List</h3>
      </template>
      <div class="flex items-center gap-2">
        <UInput
          ref="newTodoInput"
          name="todo"
          class="flex-1"
          placeholder="Add a Todo.."
          autocomplete="off"
          v-model="newTodo"
          autofocus
          :ui="{ wrapper: 'flex-1' }"
        />

        <UButton type="submit" />
      </div>
      <ul class="divide-y divide-gray-200 dark:divide-gray-800 mt-10">
        <li v-for="todo of todos"  :key="todo.id" class="flex items-center gap-4 py-2">
          <span
            class="flex-1 font-medium"
            >{{ todo.name}}</span
          >
            <UButton
            color="red"
            variant="soft"
            size="2xs"
            @click.prevent="deleTodo(todo.id)"
          />
        </li>
      </ul>
    </UCard>
  </main>
</template>

Now let’s check if our routes and features work as expected

NuxtHub

🎉 Our post route is working and our todo list is updating as expected.

Now let’s also test our delete feature

NuxtHub

🥳 Working as expected! We did it!

We successfully created a simple Todo Application leveraging NuxtHub’s in-built Database system.

Conclusion

Nuxt Hub represents a significant advancement in the Nuxt ecosystem, streamlining the development and deployment of full-stack applications. By offering zero-configuration deployment, a comprehensive set of built-in tools, and seamless integration with existing workflows, it addresses many of the complexities traditionally associated with server-side rendering and full-stack development.

The platform's robust features—such as the built-in SQL database, key-value storage, and blob storage—provide developers with the necessary components to build sophisticated applications efficiently. Additionally, its integration with Cloudflare's infrastructure ensures reliable performance and scalability.

While NuxtHub simplifies many aspects of development, it's important for developers to thoroughly understand its capabilities and limitations. As with any tool, proper planning and architecture are still crucial for successful projects.

I love working with Nuxt, and with NuxtHub, it has become even more amazing.

Charles Allotey
Charles is a Frontend Developer at Vueschool. Has a passion for building great experiences and products using Vue.js and Nuxt.

Follow MasteringNuxt on