Appwrite:开源全栈 BaaS,Firebase 之外的第三条路

Appwrite:开源全栈 BaaS,Firebase 之外的第三条路

当你在构建一个新的 Web 或移动应用时,总会面临一个重复性的工作:从零搭建用户认证、数据库 CRUD、文件存储、消息推送......这些基础设施每次都要重新来过,耗费大量时间。Firebase 解决了这个痛点,但它是 Google 的闭源服务,数据掌握在别人手中,定价也随时可能变化。Supabase 虽然是开源替代品,但它以 PostgreSQL 为核心,更偏向关系型数据库场景。

Appwrite 则走了另一条路:它是一个完全开源的 Backend-as-a-Service(BaaS),提供文档数据库、身份认证、文件存储、云函数、实时订阅等全套后端服务,通过 REST API 和官方 SDK 供前端直接调用。本文带你在自己的服务器上完整部署 Appwrite 1.5.7,并演示从创建项目到前端集成的全流程。


Appwrite vs Supabase vs Firebase

对比项 Appwrite Supabase Firebase
数据库类型 文档型(自研) PostgreSQL(关系型) Firestore(文档型)
开源 完全开源 开源 闭源
自托管 支持 支持 不支持
实时订阅 支持 支持 支持
云函数 支持(多语言) 支持(Deno/TypeScript) 支持(Node.js 为主)
适用场景 全栈应用、移动 App 数据密集型、复杂查询 快速原型

如果你的应用更像文档存储(类似 MongoDB 的使用方式),或者需要一个覆盖前端开发所有需求的一站式方案,Appwrite 是比 Supabase 更自然的选择。


服务器配置

Appwrite 内部运行了多个 worker 进程(处理函数执行、邮件发送、文件处理等),对内存有一定要求。推荐使用 2 核 4GB 机型 作为开发/测试环境,生产环境建议升级到 4 核 8GB

推荐使用 雨云服务器 rainyun-com 来部署 Appwrite,注册填 2026off 可领取 5 折券。雨云支持按需扩容,非常适合从小规模起步、逐步增长的项目。

环境要求:

  • 操作系统:Ubuntu 22.04 LTS
  • CPU:2 核及以上
  • 内存:4GB 及以上(生产环境 8GB+)
  • 磁盘:40GB 及以上(文件存储)
  • 开放端口:80、443

安装 Docker 和 Docker Compose

bash 复制代码
sudo apt update && sudo apt upgrade -y
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker

# 验证安装
docker --version
docker compose version

Docker Compose 部署 Appwrite

Appwrite 官方提供了一个交互式安装脚本,通过单条 Docker 命令拉起安装程序,自动生成完整的 docker-compose.yml 配置:

bash 复制代码
docker run -it --rm \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
  --entrypoint="install" \
  appwrite/appwrite:1.5.7

安装程序会以交互方式询问以下配置:

复制代码
? Choose your server HTTP port: 80
? Choose your server HTTPS port: 443
? Enter your Appwrite hostname: appwrite.yourdomain.com
? Enter a secret API key: (自动生成,直接回车即可)
? Enter your Appwrite database password: (设置一个强密码)
? Enter your Appwrite Redis password: (设置一个强密码)

回答完成后,安装程序会自动生成 appwrite/docker-compose.yml.env 文件,并启动所有容器。

Appwrite 内部服务架构

复制代码
appwrite          # 主 API 服务(PHP + Swoole)
appwrite-worker-audits      # 审计日志 worker
appwrite-worker-databases   # 数据库操作 worker
appwrite-worker-deletes     # 数据删除 worker
appwrite-worker-functions   # 云函数执行 worker
appwrite-worker-mails       # 邮件发送 worker
appwrite-worker-messaging   # 消息推送 worker
appwrite-worker-migrations  # 数据迁移 worker
appwrite-worker-webhooks    # Webhook worker
appwrite-traefik            # 反向代理(内置)
appwrite-mariadb            # 主数据库
appwrite-redis              # 缓存 + 队列
appwrite-influxdb           # 指标存储
appwrite-telegraf           # 指标采集

Caddy 反代配置

Appwrite 默认使用内置的 Traefik 处理 HTTPS,如果你希望统一用 Caddy 管理域名,需要修改 .env 文件关闭 Traefik 的 80/443 绑定,再配置 Caddy 代理到 Appwrite 内部端口。

安装 Caddy:

bash 复制代码
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy

修改 appwrite/.env,将端口改为非 80/443(如 8080/8443),并在 /etc/caddy/Caddyfile 中添加:

caddy 复制代码
appwrite.yourdomain.com {
    reverse_proxy localhost:8080
}

重载 Caddy:

bash 复制代码
sudo systemctl reload caddy

初始设置:创建账号与项目

访问 https://appwrite.yourdomain.com,首次打开会进入注册页面,创建管理员账号后进入控制台。

创建项目

  1. 点击 Create project
  2. 输入项目名称(如 my-todo-app
  3. 记录 Project ID(后续 SDK 初始化需要)

创建数据库与集合(Collection)

Appwrite 的数据库使用文档模型:数据库(Database)→ 集合(Collection,类似表)→ 文档(Document,类似行)。

在控制台进入 DatabasesCreate database → 命名为 main

然后创建集合 todos,添加以下属性(Attributes):

属性名 类型 必填
title String(255)
completed Boolean
userId String(255)

Settings → Permissions 中配置权限规则(如允许已登录用户读写自己的文档)。


前端集成:Web SDK 示例

安装 SDK

bash 复制代码
npm install appwrite

初始化 SDK

javascript 复制代码
// lib/appwrite.js
import { Client, Databases, Account, Storage, ID, Query } from 'appwrite';

const client = new Client()
    .setEndpoint('https://appwrite.yourdomain.com/v1')
    .setProject('YOUR_PROJECT_ID');

export const account = new Account(client);
export const databases = new Databases(client);
export const storage = new Storage(client);
export { ID, Query };

用户注册与登录

javascript 复制代码
import { account, ID } from './lib/appwrite';

// 注册新用户
async function register(email, password, name) {
    try {
        const user = await account.create(ID.unique(), email, password, name);
        // 注册成功后自动登录
        await account.createEmailPasswordSession(email, password);
        return user;
    } catch (error) {
        console.error('注册失败:', error.message);
    }
}

// 登录
async function login(email, password) {
    try {
        const session = await account.createEmailPasswordSession(email, password);
        return session;
    } catch (error) {
        console.error('登录失败:', error.message);
    }
}

// 获取当前用户信息
async function getCurrentUser() {
    try {
        return await account.get();
    } catch {
        return null;
    }
}

// 登出
async function logout() {
    await account.deleteSession('current');
}

数据库 CRUD 操作

javascript 复制代码
import { databases, ID, Query } from './lib/appwrite';

const DATABASE_ID = 'main';
const COLLECTION_ID = 'todos';

// 创建文档
async function createTodo(title, userId) {
    return await databases.createDocument(
        DATABASE_ID,
        COLLECTION_ID,
        ID.unique(),
        { title, completed: false, userId }
    );
}

// 查询当前用户的所有 Todo
async function listTodos(userId) {
    const response = await databases.listDocuments(
        DATABASE_ID,
        COLLECTION_ID,
        [
            Query.equal('userId', userId),
            Query.orderDesc('$createdAt')
        ]
    );
    return response.documents;
}

// 更新文档
async function completeTodo(documentId) {
    return await databases.updateDocument(
        DATABASE_ID,
        COLLECTION_ID,
        documentId,
        { completed: true }
    );
}

// 删除文档
async function deleteTodo(documentId) {
    return await databases.deleteDocument(DATABASE_ID, COLLECTION_ID, documentId);
}

文件存储(Storage)

创建存储桶

在控制台进入 StorageCreate bucket ,命名为 avatars,配置允许的文件类型(如 image/*)和最大文件大小。

上传与获取文件

javascript 复制代码
import { storage, ID } from './lib/appwrite';

const BUCKET_ID = 'avatars';

// 上传文件(浏览器 File 对象)
async function uploadAvatar(file) {
    const response = await storage.createFile(BUCKET_ID, ID.unique(), file);
    return response.$id;
}

// 获取文件预览 URL
function getAvatarUrl(fileId) {
    return storage.getFilePreview(BUCKET_ID, fileId, 200, 200);
}

// 下载文件
function getAvatarDownload(fileId) {
    return storage.getFileDownload(BUCKET_ID, fileId);
}

云函数(Functions)

Appwrite Functions 支持 Node.js、Python、PHP、Ruby、Dart 等多种运行时。

创建一个 Node.js 函数

在控制台进入 FunctionsCreate function ,选择 Node.js 18.0 运行时。

函数代码示例(src/index.js):

javascript 复制代码
import { Client, Databases } from 'node-appwrite';

export default async ({ req, res, log, error }) => {
    const client = new Client()
        .setEndpoint(process.env.APPWRITE_FUNCTION_API_ENDPOINT)
        .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        .setKey(req.headers['x-appwrite-key'] ?? '');

    const databases = new Databases(client);

    // 执行业务逻辑
    const body = JSON.parse(req.body || '{}');
    log(`Processing request for user: ${body.userId}`);

    return res.json({ success: true, message: 'Function executed!' });
};

触发函数

javascript 复制代码
import { Functions } from 'appwrite';
const functions = new Functions(client);

const execution = await functions.createExecution(
    'YOUR_FUNCTION_ID',
    JSON.stringify({ userId: 'user123' }),
    false  // async: false 表示同步等待结果
);
console.log(execution.responseBody);

实时订阅(Realtime)

javascript 复制代码
import { Client } from 'appwrite';

const client = new Client()
    .setEndpoint('https://appwrite.yourdomain.com/v1')
    .setProject('YOUR_PROJECT_ID');

// 订阅集合变化
const unsubscribe = client.subscribe(
    `databases.main.collections.todos.documents`,
    (response) => {
        if (response.events.includes('databases.*.collections.*.documents.*.create')) {
            console.log('新文档创建:', response.payload);
        }
        if (response.events.includes('databases.*.collections.*.documents.*.update')) {
            console.log('文档更新:', response.payload);
        }
    }
);

// 取消订阅
// unsubscribe();

功能使用:OAuth 登录配置

Appwrite 支持 30+ 种 OAuth 提供商(GitHub、Google、Discord 等)。以 GitHub 为例:

  1. 在 GitHub 创建 OAuth App,回调 URL 设为 https://appwrite.yourdomain.com/v1/account/sessions/oauth2/callback/github/YOUR_PROJECT_ID
  2. 在 Appwrite 控制台 → AuthSettings → 启用 GitHub,填入 Client ID 和 Client Secret
  3. 前端代码:
javascript 复制代码
account.createOAuth2Session(
    'github',
    'https://yourapp.com/dashboard',  // 成功回调
    'https://yourapp.com/login'        // 失败回调
);

结语

Appwrite 将原本需要数周才能搭建的后端基础设施压缩成了几小时的工作------一次部署,获得认证、数据库、存储、函数、实时功能的全套解决方案。对于独立开发者和小团队来说,这是快速验证产品想法、减少重复造轮子的利器。

相关推荐
SuperHeroWu72 小时前
【MindSpore】MindSpore 开源深度学习框架
人工智能·深度学习·开源·框架·mindspore
Are_You_Okkk_11 小时前
基于MonkeyCode解析AI研发新模式,根治开发低效痛点
大数据·人工智能·开源·ai编程
冬奇Lab15 小时前
每日一个开源项目(第116篇):FreeDomain - 让每个人都拥有属于自己的数字身份
开源
lauo15 小时前
从FunloomAI到ibbot:当你的手机不再是“手机”,而是你的AI副脑和生产节点
人工智能·智能手机·架构·开源·github
小程故事多_8017 小时前
拆解Hermes Agent技术架构,会自我迭代的开源智能体如何突破AI传统局限
人工智能·架构·开源
Hommy8818 小时前
【剪映小助手】贴纸处理接口
网络·开源·github·aigc·剪映小助手·视频剪辑自动化
敲星写码20 小时前
2026远程控制软件选购指南:按人群场景预算一站式锁定,ToDesk覆盖90%用户需求
开源
QiLinkOS20 小时前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
小丶舟20 小时前
6GB显卡跑Hermes Agent!开源AI自学习编程Agent实测
人工智能·学习·开源