Getting Started with the nuxt-directus Module
Published August 15th, 2023
In this blog post, you will explore the nuxt-directus
module and cover the setup process, and how to manage items, files, and authentication. As a member of both the Nuxt and Directus communities, I am the author and maintainer of this module and work to keep it up to date with new Directus' new features. You can read the documentation of nuxt-directus
at nuxt-directus.site.
Before you start
You will need:
- To install Node.js (v16 or newer).
- A code editor (recommended is Visual Studio Code using Volar).
- Some knowledge of Vue.js and Nuxt.
- A Directus project
Setup Nuxt
Open the terminal, intialize a new Nuxt project, and install the nuxt-directus
module:
npx nuxt init my-website
cd my-website
npm install
npm install nuxt-directus –save-dev
npx nuxt init my-website
cd my-website
npm install
npm install nuxt-directus –save-dev
You have now installed the module and created a new Nuxt project.
Create a Directus Collection
Before you can work with items on the frontend, you first have to create a collection in your Directus project.
Create a Product collection which contains a title
(string) and a price
(string) as mandatory fields, leaving the id
with default settings, which is a number
.
Configuring nuxt-directus
In your code editor open the folder my-website
. The nuxt.config.ts
file contains your Nuxt configuration - include and add the nuxt-directus
configuration:
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ["nuxt-directus"],
directus: {
url: "https://your-directus-project/"
}
})
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ["nuxt-directus"],
directus: {
url: "https://your-directus-project/"
}
})
The directus
config key is where you configure the nuxt-directus
module. Add your URL so that the module knows which Directus instance it has to connect to.
Items CRUD With nuxt-directus
The nuxt-directus
module offers two practical composables to help you work with Directus collections and items - useDirectusCollections
to manage the collections itself, and useDirectusItems
for managing the items within a collection.
Fetching Items
In the app.vue
file of your Nuxt project remove <NuxtWelcome />
and replace it with <NuxtPage />
. This tells Nuxt to use file-based routing. After that create a pages/
directory.
To fetch items, import the getItems
function from the useDirectusItems
composable. Inside of the pages/
directory, create and open an items.vue
file:
<template>
{{ products }}
</template>
<script setup lang="ts">
const { getItems } = useDirectusItems();
interface Product {
id: number;
title: string;
price: string;
}
const products = await getItems<Product>({
collection: "product"
});
</script>
<template>
{{ products }}
</template>
<script setup lang="ts">
const { getItems } = useDirectusItems();
interface Product {
id: number;
title: string;
price: string;
}
const products = await getItems<Product>({
collection: "product"
});
</script>
Note that you specify an interface and type for the Product and pass this type to the getItems
function as a Generic Type, so that the getItems
function knows what kind of items it returns. This is a first class TypeScript support 🎉
Creating Items
To create a new product, your useDirectusItems
composable also exports a createItems
function. Inside of the pages/
directory, create and open an new.vue
file:
<template>
<button @click="createItems">Create items</button>
</template>
<script setup lang="ts">
const { createItems } = useDirectusItems();
interface Product {
id: number;
title: string;
price: string;
}
const newProducts: Product[] = [
{ title: "Banana", price: "0.29€" },
{ title: "Vue.js Handbook", price: "29.99€"
];
await createItems<Product>({
collection: "product",
items: newProducts
});
</script>
<template>
<button @click="createItems">Create items</button>
</template>
<script setup lang="ts">
const { createItems } = useDirectusItems();
interface Product {
id: number;
title: string;
price: string;
}
const newProducts: Product[] = [
{ title: "Banana", price: "0.29€" },
{ title: "Vue.js Handbook", price: "29.99€"
];
await createItems<Product>({
collection: "product",
items: newProducts
});
</script>
As you can see, the createItems
method accepts an array with your item type (Product
), so you can create many items at once.
Deleting Items
To delete specific items, you must pass the IDs of the items to the deleteItems
function of useDirectusItems
. Here is an illustration of how to use it:
<script setup lang="ts">
const { deleteItems } = useDirectusItems();
const items = ["15", "20", "22"];
await deleteItems({ collection: "product", items });
</script>
<script setup lang="ts">
const { deleteItems } = useDirectusItems();
const items = ["15", "20", "22"];
await deleteItems({ collection: "product", items });
</script>
Updating Items
Use the updateItem
function, you must provide the id
and a partial items object with changes. Here is an illustration of how to use it:
<script setup lang="ts">
const { updateItem } = useDirectusItems();
interface Product {
id: number;
title: string;
price: string;
}
const product = {
price: "199.99€"
}
const updatedProduct = await updateItem<Product>({
collection: "product",
id: 1,
item: product
});
</script>
<script setup lang="ts">
const { updateItem } = useDirectusItems();
interface Product {
id: number;
title: string;
price: string;
}
const product = {
price: "199.99€"
}
const updatedProduct = await updateItem<Product>({
collection: "product",
id: 1,
item: product
});
</script>
Authentication with nuxt-directus
An important topic for almost every web application is authentication, not everyone has and should have access to certain data.
Directus offers a user & permission management system, nuxt-directus
offers as frontend counterpart the useDirectusAuth
composable to easily build a login, or even a registration page.
useDirectusAuth
implements the following functionality:
- Register / Login
- Invites
- Password Reset
- Token Refresh
Login Page
Create a new login.vue
page in your pages/
directory, and open it. You will now use thelogin()
function provided by useDirectusAuth
:
<template>
<div>
<h1>Login</h1>
<input type="email" placeholder="Your E-Mail Address" v-model="email"/>
<input type="password" placeholder="Your Password" v-model="password"/>
<button @click="onSubmit">Login</button>
</div>
</template>
<script setup lang="ts">
const { login } = useDirectusAuth();
const email = ref("");
const password = ref("");
const onSubmit = async () => {
try {
await login({ email: email.value, password: password.value });
} catch (e) {}
};
</script>
<template>
<div>
<h1>Login</h1>
<input type="email" placeholder="Your E-Mail Address" v-model="email"/>
<input type="password" placeholder="Your Password" v-model="password"/>
<button @click="onSubmit">Login</button>
</div>
</template>
<script setup lang="ts">
const { login } = useDirectusAuth();
const email = ref("");
const password = ref("");
const onSubmit = async () => {
try {
await login({ email: email.value, password: password.value });
} catch (e) {}
};
</script>
In the <script>
section, you add the logic - an onSubmit
function that gets triggered by the login button and sends a login request to Directus.
In your <template>
section you have the button and two form inputs, containing the email address and the password.
After you created the login page, you can access it with your browser on http://localhost:3000/login
(the port can change, check your nuxt console), and you will receive this:
Show User Information
The button calls the onSubmit
function on a click, but how will your application know that a user is logged in and the login was successful?
Let's add a user display so that the current user is displayed. For this purpose, you can use the useDirectusUser
composable:
<script setup lang="ts">
const { login } = useDirectusAuth();
const user = useDirectusUser();
const email = ref("");
const password = ref("");
const onSubmit = async () => {
try {
await login({ email: email.value, password: password.value });
} catch (e) {}
};
</script>
<script setup lang="ts">
const { login } = useDirectusAuth();
const user = useDirectusUser();
const email = ref("");
const password = ref("");
const onSubmit = async () => {
try {
await login({ email: email.value, password: password.value });
} catch (e) {}
};
</script>
Now you can access the user in the template area:
<template>
<div>
<h1>Login</h1>
<input type="email" placeholder="Your E-Mail Address" v-model="email"/>
<input type="password" placeholder="Your Password" v-model="password"/>
<button @click="onSubmit">Login</button>
</div>
<div style="margin-top: 55px" v-if="user">
<h1>Current User</h1>
<pre>{{ user }}</pre>
</div>
</template>
<template>
<div>
<h1>Login</h1>
<input type="email" placeholder="Your E-Mail Address" v-model="email"/>
<input type="password" placeholder="Your Password" v-model="password"/>
<button @click="onSubmit">Login</button>
</div>
<div style="margin-top: 55px" v-if="user">
<h1>Current User</h1>
<pre>{{ user }}</pre>
</div>
</template>
And if your login was successful, you should see your user object on the page.
Register and Login
After login you have the possibility to fetch the user every time Nuxt runs with the autoFetch
option and to check if the token is still active, and also it will refetch the useDirectusUser
data.
With this informations you can build a middleware to protect your pages, see the nuxt-directus docs.
Register Page
The login page works for existing users in your Directus project. You will now build a registration form for new user creation.
Create and open a newpages/register.vue
file. It looks similar to the login form, but this time your onSubmit
function calls the register
.
<template>
<div>
<h1>Registration</h1>
<input type="email" placeholder="Your E-Mail Address" v-model="email"/>
<input type="password" placeholder="Your Password" v-model="password"/>
<button @click="onSubmit">Register</button>
</div>
</template>
<script setup lang="ts">
const { register } = useDirectusAuth();
const email = ref("");
const password = ref("");
const onSubmit = async () => {
try {
await register({ email: email.value, password: password.value, });
alert("Registered successfully");
} catch (e) {}
};
</script>
<template>
<div>
<h1>Registration</h1>
<input type="email" placeholder="Your E-Mail Address" v-model="email"/>
<input type="password" placeholder="Your Password" v-model="password"/>
<button @click="onSubmit">Register</button>
</div>
</template>
<script setup lang="ts">
const { register } = useDirectusAuth();
const email = ref("");
const password = ref("");
const onSubmit = async () => {
try {
await register({ email: email.value, password: password.value, });
alert("Registered successfully");
} catch (e) {}
};
</script>
After creating your page it should look almost exactly like your login page.
The login & register is now ready, if you want to know more about authentication, and especially how nuxt-directus
works under the hood feel free to check out the documentation.
To log the user in directly after registration, combine the logic above - call the login function once after the user has been successfully created with the data the user has entered.
Files
nuxt-directus
provides the useDirectusFiles
composable, which contains two functions for working with files.
Get Files
The way you get the files is similar to all other composables. Import the getFiles
function from the useDirectusFiles composable:
<script setup>
const { getFiles } = useDirectusFiles();
const fetchFiles = async () => {
const files = await getFiles({
params: {
{ limit: 10 },
},
});
};
</script>
<script setup>
const { getFiles } = useDirectusFiles();
const fetchFiles = async () => {
const files = await getFiles({
params: {
{ limit: 10 },
},
});
};
</script>
Limit query
The limit
option is a global search queries which you can use on Items too, checkout the DirectusQueryParams Type for more information about the search queries.
Thumbnails
For images that are located at Directus and should also be displayed, nuxt-directus
provides the getThumbnail
function which returns an image url src:
<template>
<div>
<img :src="getThumbnail(fileId)" alt="original" />
</div>
</template>
<script setup>
const fileId = "5e47b7e6-fd78-400c-821f-0dca4a176f4f";
const { getThumbnail } = useDirectusFiles();
</script>
<template>
<div>
<img :src="getThumbnail(fileId)" alt="original" />
</div>
</template>
<script setup>
const fileId = "5e47b7e6-fd78-400c-821f-0dca4a176f4f";
const { getThumbnail } = useDirectusFiles();
</script>
Summary
nuxt-directus
has eight integrated composables, in this post, you worked with four. While there is more to learn, you have learned the most important ones - you can now build beautiful Authentication pages, build an application based on your Directus Data models, Items and Collections.
If you want to learn more about nuxt-directus
check out the documentation, if you have questions feel free to ask them on the Nuxt Discord or on GitHub as an Issue or Discussion.