@websideproject/nuxt-auto-api
| Install | |
|---|---|
npm install @websideproject/nuxt-auto-api |
|
| Latest Version: | 0.1.5 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 18, 2026 |
| Links: | GitHub · npm |
@websideproject/nuxt-auto-api
Schema-driven REST APIs for Nuxt. Register a Drizzle table, declare who may do what, and get list / get / create / update / delete, bulk, aggregations, many-to-many and permission endpoints — plus TanStack Query composables for the client.
- Secure by default — every operation is denied until you declare who may perform it
- Row visibility everywhere — tenant scoping,
listFilter, soft delete andobjectLevelapply to every route: list, get, update, delete, restore, bulk, aggregate, M2M and?include= - Server-owned columns — tenant, soft-delete and audit columns (and the primary key on update, or on create when the database generates it) can never be written by a request body
- Filtering, sorting, keyset pagination, nested includes, aggregations — restricted to columns the caller may read
- Soft delete, cascade and restore, field-level permissions, lifecycle hooks, a plugin system
- SQLite, D1, Turso, Postgres, MySQL, PlanetScale
- Nuxt 4 (and Nuxt 5 nightly)
Install
npx nuxt module add @websideproject/nuxt-auto-api
npm install drizzle-orm better-sqlite3 # Drizzle + your database's driver; auto-api uses this same Drizzle
Quick start
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@websideproject/nuxt-auto-api', './modules/blog'],
})
// server/plugins/database.ts — hand auto-api your Drizzle instance and name the engine
import Database from 'better-sqlite3'
import { drizzle } from 'drizzle-orm/better-sqlite3'
import { initializeDatabase } from '@websideproject/nuxt-auto-api/database'
import * as schema from '../../modules/blog/schema'
export default defineNitroPlugin(() => {
initializeDatabase(drizzle(new Database('sqlite.db'), { schema }), 'better-sqlite3') // or 'postgres', 'mysql', 'd1', 'turso', 'planetscale'
})
// modules/blog/schema.ts — a Drizzle table, as you'd write it anyway
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
published: integer('published', { mode: 'boolean' }).notNull().default(false),
authorId: text('author_id'),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
})
// modules/blog/index.ts — register resources from a module
import { defineNuxtModule, createResolver } from '@nuxt/kit'
import { createModuleImport } from '@websideproject/nuxt-auto-api'
export default defineNuxtModule({
// Nuxt also loads modules/*/index.ts by itself; the name lets it see the entry in nuxt.config is the same module
meta: { name: 'blog' },
setup(_, nuxt) {
const { resolve } = createResolver(import.meta.url)
nuxt.hook('autoApi:registerSchema', (registry) => {
registry.register('posts', {
schema: createModuleImport(resolve('./schema'), 'posts'),
authorization: createModuleImport(resolve('./auth'), 'postsAuth'),
})
})
},
})
// modules/blog/auth.ts
import { eq, or } from 'drizzle-orm'
import type { ResourceAuthConfig } from '@websideproject/nuxt-auto-api'
export const postsAuth: ResourceAuthConfig = {
permissions: {
read: true, // anyone
create: ctx => !!ctx.user, // signed-in users
update: ctx => !!ctx.user,
delete: 'posts:delete', // callers holding this permission
},
// Which rows a caller may see — applied to EVERY route, not only lists.
listFilter: (posts, ctx) => ctx.user ? or(eq(posts.published, true), eq(posts.authorId, ctx.user.id)) : eq(posts.published, true),
// Per-row check: anyone may read a visible post; only its author may change or delete it.
objectLevel: (post, ctx) => ctx.operation === 'list' || ctx.operation === 'get' || post.authorId === ctx.user?.id,
}
That's GET/POST /api/posts, GET/PATCH/DELETE /api/posts/:id, /api/posts/bulk, /api/posts/aggregate,
/api/posts/:id/relations/:relation, /api/posts/permissions and /api/permissions.
<script setup lang="ts">
const { data } = useAutoApiList('posts', { sort: '-createdAt', limit: 20 })
const { mutate: createPost } = useAutoApiCreate('posts')
</script>
Security model
| Undeclared operation | Denied (401 anonymous / 403 signed in). true = public, false = nobody — not even * |
* permission |
Super-admin: passes everything except an explicit false |
| Row visibility | Tenant + listFilter + soft delete on every route; objectLevel per row |
| Request bodies | Tenant, soft-delete and audit columns are dropped (the primary key on update too, and on create when the database generates it); field-level write rules refuse with 403 |
| Queries | filter / sort / groupBy / aggregates / include filters may only name readable columns — unknown fields and operators are 400s |
?include= |
Needs read on the related resource and applies its row visibility and field rules |
| Tenancy | Resolved on the server only (event.context.tenantId, ctx.user.organizationId, or an extender); fails closed |
In the playground
The repo's playground runs these on a seeded database; the pictures are its CI screenshots.
Roles, rows and fields. A regular user's permissions, from GET /api/permissions:

Nested includes with field selection and a limit:
![The include builder: author fields id and name, limit 2, the query /api/posts?include=author[id,name]&limit=2 and its response](https://raw.githubusercontent.com/websideproject/nuxt-auto/main/apps/docs/public/screenshots/api-nested-relations-1440.png)
Aggregations grouped by a column:

Scoped API tokens: an editor token may read articles and nothing it holds no scope for:

Documentation
Full documentation: websideproject.com/docs/nuxt-auto
- Getting started
- Authentication & authorization · Permissions cookbook · better-auth
- Multi-tenancy · Soft deletes · Validation
- Pagination · Nested relationships · Bulk operations · Aggregations · M2M
- Frontend composables · Custom endpoints · Lifecycle hooks
- Plugin system · Plugin catalog · Rate limiting
- Database adapters · Cloudflare D1 · Testing
- Configuration · Upgrading
An admin panel for these resources: @websideproject/nuxt-auto-admin.
Links
License
Related Packages
API-driven dynamic CRUD table and forms in Nuxt.js. Automatically reflects backe...
Nuxt module that signs your app in with Authup and guards its routes by login st...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.5 | ^3.0 | MIT | |
| 0.1.4 | ^3.0 | MIT | |
| 0.1.3 | ^3.0 | MIT | |
| 0.1.2 | ^3.0 | MIT | |
| 0.1.1 | ^3.0 | MIT | |
| 0.1.0 | ^3.0 | MIT | |
| 0.0.11 | ^3.0 | MIT | |
| 0.0.10 | ^3.0 | MIT | |
| 0.0.9 | ^3.0 | MIT | |
| 0.0.8 | ^3.0 | MIT | |
| 0.0.7 | ^3.0 | MIT | |
| 0.0.6 | ^3.0 | MIT | |
| 0.0.3 | ^3.0 | MIT | |
| 0.0.2 | ^3.0 | MIT | |
| 0.0.1 | ^3.0 | MIT |