Getting Started with Nuxt 3
The content of this page has not been updated to Strapi v5 yet.
This integration guide follows the Quick Start Guide and assumes you have you have fully completed the "Hands-on" path. You should be able to consume the API by browsing the URL http://localhost:1337/api/restaurants.
If you haven't gone through the Quick Start Guide, the way you request a Strapi API with Nuxt 3 remains the same except that you do not fetch the same content.
Create a Nuxt 3 app
Create a basic Nuxt 3 application with npx package runner
npx nuxi init nuxt-app
Use an HTTP client
For this example we are using the awesome @nuxt/strapi module and Nuxt helper function $fetch (based on ohmyfetch
). You may choose any of this variants.
- @nuxtjs/strapi
- fetch
yarn add --dev @nuxtjs/strapi
Add @nuxtjs/strapi
to the module section of nuxt.config.ts
with the following settings:
modules: ['@nuxtjs/strapi'],
strapi: {
url: 'http://localhost:1337'
},
No installation needed.
GET Request - get list of entities or one entity
Execute a GET
request on the restaurant
collection type in order to fetch your restaurants.
Be sure that you activated the find
and findOne
permission for the restaurant
collection type.
- @nuxtjs/strapi
- fetch
@nuxtjs/strapi
exposes composables that are auto-imported by Nuxt 3. Note that delete
function must be renamed because it's reserved word in JavaScript.
<script setup lang="ts">
import type { Restaurant } from '~/types'
const { find, findOne, create, update, delete: remove } = useStrapi()
</script>
// Get all restaurants
const response = await find<Restaurant>('restaurants')
// Get one restaurant by id
const response = await findOne<Restaurant>("restaurants", restaurantId)
// Get all restaurants
const response = $fetch("http://localhost:1337/api/restaurants")
// Get one restaurant by id
const response = await $fetch(`http://localhost:1337/api/restaurants/${restaurantId}`)
// List of the restaurants
{
"data": [
{
"id": 1,
"attributes": {
"name": "Biscotte Restaurant",
"description": "Welcome to Biscotte restaurant! \nRestaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers.",
"createdAt": "2022-10-29T09:37:55.061Z",
"updatedAt": "2022-11-07T18:53:27.702Z",
"publishedAt": "2022-10-29T09:47:48.782Z"
}
},
{
"id": 2,
"attributes": {
"name": "Dolemon Sushi",
"description": "Unmissable Japanese Sushi restaurant. The cheese and salmon makis are delicious.",
"createdAt": "2022-10-29T09:37:55.061Z",
"updatedAt": "2022-11-07T18:53:27.702Z",
"publishedAt": "2022-10-29T09:47:48.782Z"
}
}
],
"meta": {
"pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 }
}
}
// One restaurant
{
"data": {
"id": 1,
"attributes": {
"name": "Biscotte Restaurant",
"description": "Welcome to Biscotte restaurant! \nRestaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers.",
"createdAt": "2022-10-29T09:37:55.061Z",
"updatedAt": "2022-11-07T18:53:27.702Z",
"publishedAt": "2022-10-29T09:47:48.782Z"
}
},
"meta": {}
}
POST Request - create new entity
Execute a POST
request on the restaurant
collection type in order to create a restaurant.
Be sure that you activated the create
permission for the restaurant
collection type.
- @nuxtjs/strapi
- fetch
await create<Restaurant>("restaurants", {
name: restaurantName,
description: restaurantDescription })
await $fetch("http://localhost:1337/api/restaurants", {
method: "POST",
body: {
data: {
name: restaurantName,
description: restaurantDescription
}
}
})
PUT Request - update existing entity
Execute a PUT
request on the restaurant
collection type in order to update your restaurant.
Be sure that you activated the put
permission for the restaurant
collection type.
- @nuxtjs/strapi
- fetch
await update<Restaurant>("restaurants", restaurantId, {
name: restaurantName,
description: restaurantDescription })
await $fetch(`http://localhost:1337/api/restaurants/${restaurantId}`, {
method: "PUT",
body: {
data: {
name: restaurantName,
description: restaurantDescription
}
}
})
DELETE Request - delete existing entity
Execute a DELETE
request on the restaurant
collection type in order to delete your restaurant.
Be sure that you activated the delete
permission for the restaurant
collection type.
- @nuxtjs/strapi
- fetch
await remove<Restaurant>("restaurants", restaurantId);
await $fetch(`http://localhost:1337/api/restaurants/${restaurantId}`, {
method: 'DELETE'
})
Example
Consider an example of a simple CRUD Nuxt application that implements the functions described above.
- @nuxtjs/strapi
- fetch
./pages/index.vue
<template>
<div>
<ul>
<li v-for="restaurant in restaurants?.data" :key="restaurant.id">
{{ restaurant.attributes.name }}
<button @click="$router.push(`${$route.path}/restaurant/${restaurant.id}`)">Edit</button>
<button @click="deleteRestaurant(restaurant.id)">Delete</button>
</li>
</ul>
<nuxt-link :to="`${$route.path}/restaurant/create`">Create</nuxt-link>
</div>
</template>
<script setup lang="ts">
import type { Restaurant } from '~/types'
const { find, delete: remove } = useStrapi() // delete is keyword in JS, must not be used
const { data: restaurants, refresh } = await useAsyncData(
'restaurants',
() => find<Restaurant>('restaurants')
)
const deleteRestaurant = async (restaurantId: number) => {
await remove<Restaurant>("restaurants", restaurantId);
refresh()
};
</script>
./pages/restaurant/create.vue
<template>
<div>
<div><input type="text" v-model="name" /></div>
<div><textarea v-model="description"></textarea></div>
<button @click="createRestaurant();$router.go(-1)">Create</button>
<button @click="$router.go(-1)">Cancel</button>
</div>
</template>
<script setup lang="ts">
import type { Restaurant } from "~/types"
const { create } = useStrapi()
const name = ref("")
const description = ref("")
const createRestaurant = async () => {
await create<Restaurant>("restaurants", {
name: name.value,
description: description.value
})
</script>
./pages/restaurant/[id].vue
<template>
<div>
<div><input type="text" v-model="name" /></div>
<div><textarea v-model="description"></textarea></div>
<button @click="updateRestaurant();$router.go(-1)">Update</button>
<button @click="$router.go(-1)">Cancel</button>
{{ restaurant }}
</div>
</template>
<script setup lang="ts">
import type { Restaurant } from '~/types'
const { findOne, update } = useStrapi()
const route = useRoute()
const restaurantId: number = +route.params.id // cast to number
const response = await findOne<Restaurant>("restaurants", restaurantId)
const name = ref(response.data.attributes.name)
const description = ref(response.data.attributes.description)
const updateRestaurant = async () => {
await update<Restaurant>("restaurants", restaurantId, {
name: name.value,
description: description.value
})
}
</script>
./pages/index.vue
<template>
<div>
<ul>
<li v-for="restaurant in restaurants.data" :key="restaurant.id">
{{ restaurant.attributes.name }}
<button @click="$router.push(`${$route.path}/restaurant/${restaurant.id}`)">Edit</button>
<button @click="deleteRestaurant(restaurant.id)">Delete</button>
</li>
</ul>
<div v-else>Loading...</div>
<nuxt-link :to="`${$route.path}/restaurant/create`">Create</nuxt-link>
</div>
</template>
<script setup>
const { data: restaurants, refresh } = await useAsyncData(
'restaurants',
() => $fetch("http://localhost:1337/api/restaurants")
)
const deleteRestaurant = async (restaurantId) => {
await $fetch(`http://localhost:1337/api/restaurants/${restaurantId}`, {
method: 'DELETE'
})
refresh()
}
</script>
./pages/restaurant/create.vue
<template>
<div>
<div><input type="text" v-model="restaurant.name" /></div>
<div><textarea v-model="restaurant.description"></textarea></div>
<button @click="createRestaurant();$router.go(-1)">Create</button>
<button @click="$router.go(-1)">Cancel</button>
{{ restaurant }}
</div>
</template>
<script setup>
const restaurant = ref({ name: "", description: "" })
const createRestaurant = async () => {
await $fetch("http://localhost:1337/api/restaurants", {
method: "POST",
body: {
data: {
name: restaurant.value.name,
description: restaurant.value.description
}
}
})
}
</script>
./pages/restaurant/[id].vue
<template>
<div>
<div><input type="text" v-model="restaurant.name" /></div>
<div><textarea v-model="restaurant.description"></textarea></div>
<button @click="updateRestaurant();$router.go(-1)">Update</button>
<button @click="$router.go(-1)">Cancel</button>
{{ restaurant }}
</div>
</template>
<script setup>
const route = useRoute()
const response = await $fetch(`http://localhost:1337/api/restaurants/${route.params.id}`)
const restaurant = ref(response.data.attributes)
const updateRestaurant = async () => {
await $fetch(`http://localhost:1337/api/restaurants/${route.params.id}`, {
method: "PUT",
body: {
data: {
name: restaurant.value.name,
description: restaurant.value.description
}
}
})
}
</script>