Хранение данных на сервере
Список в памяти сервера пропадает при каждом перезапуске. Чтобы статьи сохранялись, их нужно где-то хранить. В Nuxt для этого есть встроенное хранилище Nitro — useStorage. Это хранилище «ключ — значение»: под строковым ключом лежит любое значение, которое можно записать в JSON. Хранилище подключается к разным местам — папке на диске, Redis, облаку; в курсе используется папка на диске.
Настройка
Опишите хранилище в nuxt.config.ts:
// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
nitro: {
storage: {
blog: { driver: 'fs', base: './.data/blog' }
}
}
})Хранилище получило имя blog, драйвер fs пишет данные в файлы, base — папка для них. Добавьте строку .data в файл .gitignore, чтобы данные не попали в git, и перезапустите npm run dev.
Чтение и запись
// server/api/visits.get.ts
export default defineEventHandler(async () => {
const storage = useStorage('blog')
const visits = ((await storage.getItem<number>('visits')) ?? 0) + 1
await storage.setItem('visits', visits)
return { visits }
})getItem читает значение по ключу (или null, если его нет), setItem записывает. Обновляйте /api/visits — число растёт и не сбрасывается после перезапуска сервера: оно лежит в файле .data/blog/visits.
Статьи на диске
Замените server/utils/posts.ts:
// server/utils/posts.ts
export interface Post {
id: number
title: string
text: string
}
export interface PostComment {
author: string
text: string
}
export async function getPosts() {
return (await useStorage('blog').getItem<Post[]>('posts')) ?? []
}
export async function savePosts(posts: Post[]) {
await useStorage('blog').setItem('posts', posts)
}Теперь обработчики берут статьи через эти функции:
// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody<{ title?: string; text?: string }>(event)
if (!body.title || !body.text) {
throw createError({ statusCode: 400, message: 'Заполните заголовок и текст' })
}
const posts = await getPosts()
const post = {
id: Math.max(0, ...posts.map((p) => p.id)) + 1,
title: body.title,
text: body.text
}
posts.push(post)
await savePosts(posts)
setResponseStatus(event, 201)
return post
})В server/api/posts.get.ts и server/api/posts/[id].get.ts замените обращение к массиву posts строкой const posts = await getPosts() и сделайте обработчики async. Добавленные статьи переживут перезапуск.
Комментарии
Комментарии каждой статьи храните под своим ключом. Тип PostComment описан выше в server/utils/posts.ts:
// server/api/posts/[id]/comments.get.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id')
return (await useStorage('blog').getItem<PostComment[]>(`comments:${id}`)) ?? []
})// server/api/posts/[id]/comments.post.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id')
const body = await readBody<{ author?: string; text?: string }>(event)
if (!body.author || !body.text) {
throw createError({ statusCode: 400, message: 'Укажите имя и текст' })
}
const storage = useStorage('blog')
const comments = (await storage.getItem<PostComment[]>(`comments:${id}`)) ?? []
comments.push({ author: body.author, text: body.text })
await storage.setItem(`comments:${id}`, comments)
setResponseStatus(event, 201)
return comments
})Ключ с двоеточием comments:1 драйвер fs превращает в файл .data/blog/comments/1. Список комментариев первой статьи — по адресу /api/posts/1/comments.
Когда нужна настоящая база данных
Хранилище «ключ — значение» хорошо для небольшого блога. Если нужны поиск по полям, связи и много записей, подключают базу данных — SQLite, PostgreSQL или MySQL — через её библиотеку для Node.js. Обращаются к ней так же из обработчиков в server/.
Частая ошибка
Вызвать useStorage('blog'), не описав хранилище в nitro.storage. Ошибки не будет, но Nitro поместит такие данные в память, и после перезапуска они пропадут. Если данные исчезают, проверьте настройку и перезапустите сервер после правки nuxt.config.ts.