Self-Hosting, DevOps, AppDev, UI, UX, API, Nuxt, Coolify··x·x

Sneak Preview: Nuxt Coolify Module

A Nuxt module for Coolify, it wraps the Coolify API and provides a set of server utilities for Nuxt and Nitro.

Introduction

Over the last few months I've been working on a Nuxt module for Coolify, it wraps the Coolify API and provides a set of server utilities for Nuxt and Nitro. Thanks to Sandros94 and his contributions to the project, the module has improved a lot.

The module is however still in the early stages of development, but it already provides a set of useful utilities. It injects our Nitro server utilities to your Nuxt project, which can be used to interact with the Coolify API from your Nitro server endpoints.

Quickstart

Use npx nuxi module add nuxt-coolify to automaticly add the module to your project or, Install nuxt-coolify using your package manager of choice and add it manually to your modules: ['nuxt-coolify'] array within your nuxt.config.ts file.

Nuxt Coolify Module

Setup Overview

nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-coolify']
})
.env
NUXT_COOLIFY_INSTANCES_DEFAULT_BASE_URL=<your-coolify-url>
NUXT_COOLIFY_INSTANCES_DEFAULT_API_TOKEN=<your-coolify-api-token>
NUXT_COOLIFY_PROVIDERS_HETZNER_BASE_URL=<your-hetzner-api-url>
NUXT_COOLIFY_PROVIDERS_HETZNER_API_TOKEN=<your-hetzner-api-token>
You can get your Coolify API token from the Coolify dashboard under Settings -> API Tokens.
You can get your Hetzner API token from the Hetzner Cloud dashboard under Security -> API Tokens.

Usage Example in a Nitro endpoint

server/api/v1/_coolify/_instances.ts
// List all instances
export default defineEventHandler(async (event) => {
  // check auth permissions
  return useCoolify().instances()
})
server/api/v1/_coolify/_instances/:name/index.get.ts
// List all resources for a specific Coolify instance
import type { Instance } from 'nuxt-coolify'

export default defineEventHandler(async (event) => {
  const name = getRouterParam(event, 'name')
  const { instances } = useRuntimeConfig().coolify

  if (name && !Object.prototype.hasOwnProperty.call(instances, name)) {
    return createError({
      statusCode: 500,
      message: 'Provided Instance is not configured.',
    })
  }
  // check auth permissions
  return useCoolify().instances(name as Instance)
})

Nuxt Coolify Examples Project

Nuxt Coolify Example Project

This example project demonstrates how to use the Nuxt Coolify Module to interact with the Coolify API and Nitro server.

How to run the example project

You can find the example project on GitHub.

  1. Clone the repository and install the dependencies.
  2. Create a .env file and add your Coolify and Hetzner API tokens and API URLs if you haven't already.
.env
NUXT_COOLIFY_INSTANCES_DEFAULT_BASE_URL=https://coolify.yourdomain.com/api/v1
NUXT_COOLIFY_INSTANCES_DEFAULT_API_TOKEN=<your-coolify-api-token>
NUXT_COOLIFY_PROVIDERS_HETZNER_BASE_URL=https://api.hetzner.cloud/v1
NUXT_COOLIFY_PROVIDERS_HETZNER_API_TOKEN=<your-hetzner-api-token>
You can get your Coolify API token from the Coolify dashboard under Settings -> API Tokens.
You can get your Hetzner API token from the Hetzner Cloud dashboard under Security -> API Tokens.
  1. Run the development server and open the page in your browser.
pnpm dev

How to use the module on a page

In the index.vue file you can see how we interact with the API and Nitro server.

pages/index.vue
<script setup>
// Fetch all instances
const { data: instances } = await useFetch("/api/v1/coolify/instances", {
  method: "GET",
  headers: { "Content-Type": "application/json" },
});
</script>

<template>
  ...
  <UDashboardPanelContent>
    <UDashboardSection
      icon="i-heroicons-user"
      title="Welcome"
      description="Manage your instances and servers."
      orientation="vertical"
      class="px-4 mt-6"
    />
    <div class="grid lg:grid-cols-2 lg:items-start gap-8 mt-8">
      <!-- List all instances -->
      <HomeInstances :instances="instances" />
      <!-- List all locations -->
      <HomeLocations />
    </div>
  </UDashboardPanelContent>
  ...
</template>

On the left side you can see the HomeInstances component which lists all your Coolify instances and on the right side you can see the HomeLocations component which lists all Hetzner locations. Nuxt Coolify Example Page and Components

The HomeInstances component is a simple component that lists all your Coolify instances and provides a link to each instance it got back from the API. We are calling the /api/v1/coolify/instances endpoint to get the list of instances. The endpoint can be found in the server folder of the example project.

You can use our built in Nitro server utilities like useCoolify().instances() to get the list of your default instance.

server/api/v1/coolify/instances.get.ts
// List all instances
export default defineEventHandler(async (event) => {
  // TODO: Add your own auth check here

  const instances = await useCoolify().instances()
  return instances[0].default
})

You can also grab a specific instance by sending the instance's UUID. E.g:

  1. Create a new Nitro server endpoint: server/api/v1/coolify/instances/:id/index.get.ts.
  2. Use the useCoolify().instances() utility function to get the list of instances and find the instance with the matching UUID.
server/api/v1/coolify/instances/:id/index.get.ts
// server/api/v1/coolify/instances/[id]/index.get.ts
export default defineEventHandler(async (event) => {
  // TODO: Add your own auth check here

  const uuid = getRouterParam(event, 'id')
  const instances = await useCoolify().instances()
  return instances[0].default.find((instance) => instance.uuid === uuid)
})

We also have the Hetzner API wrapped in a utility function that you can use to get the list of locations of their datacenters.

  1. Create a new Nitro server endpoint: server/api/v1/hetzner/locations.get.ts.
  2. Use the useHetzner().getDatacenters() utility function to get the list of locations. E.g:
server/api/v1/hetzner/locations.get.ts
export default defineEventHandler(async (event) => {
  return useHetzner().getDatacenters()
})

How to use the module in a component

You can use the built in useFetch, useLazyFetch or useAsyncData function to fetch data from the API. E.g:

components/HomeLocations.vue
<script setup lang="ts">
const { data: locations } = await useLazyFetch('/api/v1/hetzner/locations')
</script>

<template>
  <UDashboardCard
    title="Hetzner"
    description="Datacenter Locations"
    icon="simple-icons:hetzner"
  >
    <NuxtLink
      v-for="(datacenter, index) in locations.datacenters"
      :key="index"
      class="px-3 py-2 -mx-2 last:-mb-2 rounded-md hover:bg-gray-50 dark:hover:bg-gray-800/50 cursor-pointer flex items-center gap-3 relative"
    >

      <div class="text-sm flex-1">
        <div>
          <p class="text-gray-900 dark:text-white font-medium">
            {{ datacenter.location.city }}
          </p>
          <p class="text-gray-500 dark:text-gray-400">
            {{ datacenter.location.network_zone }}
          </p>
        </div>
      </div>

      <p class="text-gray-900 dark:text-white font-medium text-lg">
        +
      </p>
    </NuxtLink>
  </UDashboardCard>
</template>

More fetch examples

components/YourComponent.vue
<script setup lang="ts">
const { data: instances, status, refresh, error } = await useFetch('/api/v1/coolify/instances')
</script>
components/YourComponent.vue
<script setup lang="ts">
const { data: instances, status, refresh, error } = await useLazyFetch('/api/v1/coolify/instances')
</script>
components/YourComponent.vue
<script setup lang="ts">
const { data: instances, status, refresh, error } = await useAsyncData('instances', () => 
  $fetch('/api/v1/coolify/instances')
)
</script>

Learn more about Nuxt Data Fetching.

Parallel and sequential fetching with useAsyncData() and Promise.all()

Fetching in parallel:

pages/YourPage.vue
<script setup lang="ts">
const { data, pending, error, refresh } = await useAsyncData('resources', async () => {
  const [instances, locations] = await Promise.all([
    const instances = await $fetch('/api/v1/coolify/instances'),
    const locations = await $fetch('/api/v1/hetzner/locations')
  ])
  return { instances, locations }
})
</script>

Sequential fetching

Fetching resources sequentially in order if the second request depends on response of the first request.

The fetching order depends on the order of the await keyword.
components/YourComponent.vue
<script setup lang="ts">
const { data, pending, error, refresh } = await useAsyncData('resources', async () => {
  const instances = await $fetch('/api/v1/coolify/instances')
  const locations = await $fetch('/api/v1/hetzner/locations')
  return { instances, locations }
})
</script>

Endless Possibilities

We provide a set of Nitro server utilities that can be used to interact with the Coolify and Hetzner APIs (more providers coming soon).

Here is another example of how you can use the module to create a interactive map of all your Coolify instances and VPS provider datacenter locations like that of Hetzner. Nuxt Coolify Module Designs

Stay tuned for more providers, improvements and features!

Resources


Services

Copyright © 2024. All rights reserved.