Switch to Appwrite Databases
How to change the database provider to Appwrite Databases.
Appwrite Databases is a document database service that's part of the Appwrite platform. It provides a flexible schema system with collections, documents, and built-in permissions.
next-forge uses Neon as the database provider with Prisma as the ORM. This guide will help you switch from Neon and Prisma to Appwrite Databases.
Appwrite uses a document database model, not SQL. There is no Prisma ORM equivalent — you'll use Appwrite's SDK and Query builder instead. Your data model will need to be restructured around collections and documents rather than relational tables.
1. Replace the database package dependencies
Uninstall the existing Neon and Prisma dependencies from the database package...
npm uninstall @neondatabase/serverless @prisma/adapter-neon @prisma/client prisma ws @types/ws --filter @repo/database...and install the Appwrite dependency:
npm install node-appwrite --filter @repo/database2. Update environment variables
Add the following environment variables to your .env.local file. You can find these values in your Appwrite project's Settings page:
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT_ID=your-project-id
APPWRITE_API_KEY=your-api-key
APPWRITE_DATABASE_ID=your-database-idYou'll need to create a database in the Appwrite Console first. The APPWRITE_DATABASE_ID is the ID of the database you create.
3. Update the environment keys
Update the keys.ts file to validate the new environment variables:
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const keys = () =>
createEnv({
server: {
APPWRITE_API_KEY: z.string().min(1),
APPWRITE_DATABASE_ID: z.string().min(1),
},
client: {
NEXT_PUBLIC_APPWRITE_ENDPOINT: z.string().url(),
NEXT_PUBLIC_APPWRITE_PROJECT_ID: z.string().min(1),
},
runtimeEnv: {
APPWRITE_API_KEY: process.env.APPWRITE_API_KEY,
APPWRITE_DATABASE_ID: process.env.APPWRITE_DATABASE_ID,
NEXT_PUBLIC_APPWRITE_ENDPOINT:
process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT,
NEXT_PUBLIC_APPWRITE_PROJECT_ID:
process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID,
},
});4. Update the database package
Replace the contents of index.ts with a configured Appwrite Databases client:
import 'server-only';
import { Client, Databases, Query, ID, Permission, Role } from 'node-appwrite';
const client = new Client()
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!)
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!)
.setKey(process.env.APPWRITE_API_KEY!);
export const database = new Databases(client);
export const databaseId = process.env.APPWRITE_DATABASE_ID!;
export { Query, ID, Permission, Role };5. Remove Prisma files
Delete the Prisma-specific files that are no longer needed:
rm -rf packages/database/prisma packages/database/prisma.config.tsAlso remove any Prisma-related scripts from your package.json files (e.g., migrate, generate, studio).
6. Understanding Appwrite's data model
Appwrite organizes data differently from SQL databases:
| SQL (Prisma) | Appwrite |
|---|---|
| Database | Database |
| Table | Collection |
| Row | Document |
| Column | Attribute |
| Migration | Console / SDK |
Collections are defined with typed attributes (string, integer, boolean, float, email, enum, URL, IP, datetime, relationship) and can be created via the Appwrite Console or programmatically with the SDK.
7. Create collections
You can create collections through the Appwrite Console, or programmatically using the server SDK. Here's an example of creating a posts collection:
import { database, databaseId, ID, Permission, Role } from '@repo/database';
await database.createCollection(
databaseId,
ID.unique(),
'posts',
[
Permission.read(Role.any()),
Permission.create(Role.users()),
Permission.update(Role.users()),
Permission.delete(Role.users()),
]
);Then define its attributes:
const collectionId = 'your-collection-id';
await database.createStringAttribute(
databaseId,
collectionId,
'title',
255,
true // required
);
await database.createStringAttribute(
databaseId,
collectionId,
'content',
10000,
false // optional
);
await database.createStringAttribute(
databaseId,
collectionId,
'authorId',
255,
true
);
await database.createDatetimeAttribute(
databaseId,
collectionId,
'publishedAt',
false
);For most projects, it's easier to create collections and attributes through the Appwrite Console UI rather than programmatically.
8. Perform CRUD operations
Here's how to perform basic CRUD operations with the Appwrite SDK:
Create a document
import { database, databaseId, ID } from '@repo/database';
const post = await database.createDocument(
databaseId,
'posts', // collection ID
ID.unique(),
{
title: 'Hello World',
content: 'This is my first post.',
authorId: 'user-123',
publishedAt: new Date().toISOString(),
}
);Read documents
import { database, databaseId, Query } from '@repo/database';
// Get a single document
const post = await database.getDocument(
databaseId,
'posts',
'document-id'
);
// List documents with filters
const posts = await database.listDocuments(
databaseId,
'posts',
[
Query.equal('authorId', 'user-123'),
Query.orderDesc('publishedAt'),
Query.limit(10),
]
);Update a document
import { database, databaseId } from '@repo/database';
const updated = await database.updateDocument(
databaseId,
'posts',
'document-id',
{
title: 'Updated Title',
}
);Delete a document
import { database, databaseId } from '@repo/database';
await database.deleteDocument(
databaseId,
'posts',
'document-id'
);9. Permissions
Appwrite uses a document-level permissions system instead of SQL's Row Level Security. You can set permissions when creating or updating documents:
import { database, databaseId, ID, Permission, Role } from '@repo/database';
const post = await database.createDocument(
databaseId,
'posts',
ID.unique(),
{
title: 'Private Post',
content: 'Only I can see this.',
authorId: 'user-123',
},
[
Permission.read(Role.user('user-123')),
Permission.update(Role.user('user-123')),
Permission.delete(Role.user('user-123')),
]
);Common permission patterns:
Role.any()— Anyone (including guests)Role.users()— Any authenticated userRole.user('userId')— A specific userRole.team('teamId')— Members of a specific teamRole.team('teamId', 'admin')— Team members with a specific role
10. Update your apps
Replace Prisma queries throughout your application with Appwrite SDK calls:
// Before (Prisma)
import { database } from '@repo/database';
const posts = await database.post.findMany({
where: { authorId: userId },
orderBy: { createdAt: 'desc' },
});
// After (Appwrite)
import { database, databaseId, Query } from '@repo/database';
const { documents: posts } = await database.listDocuments(
databaseId,
'posts',
[
Query.equal('authorId', userId),
Query.orderDesc('$createdAt'),
Query.limit(25),
]
);Additional features
Realtime subscriptions
Appwrite supports realtime subscriptions on the client side. You can listen for changes to documents:
import { client } from '@repo/auth/client';
const unsubscribe = client.subscribe(
`databases.${databaseId}.collections.posts.documents`,
(response) => {
// Handle realtime event
console.log(response);
}
);Relationships
Appwrite supports relationships between collections. You can create one-to-one, one-to-many, and many-to-many relationships via the Console or SDK:
import { database, databaseId } from '@repo/database';
await database.createRelationshipAttribute(
databaseId,
'posts',
'comments',
'oneToMany',
false,
'postId',
'comments'
);Indexes
For better query performance, create indexes on frequently queried attributes:
import { database, databaseId } from '@repo/database';
await database.createIndex(
databaseId,
'posts',
'idx_authorId',
'key',
['authorId']
);For more information, see the Appwrite Databases documentation.