Foxx微服务 #

一、Foxx概述 #

Foxx是ArangoDB内置的微服务框架,允许在数据库内部运行JavaScript应用。

1.1 Foxx优势 #

优势 说明
高性能 直接访问数据库,无网络开销
安全 数据不离开数据库
简单 使用JavaScript开发
集成 与AQL无缝集成

1.2 Foxx服务结构 #

text
my-service/
├── manifest.json    # 服务配置
├── index.js         # 主入口文件
├── routes/          # 路由文件
│   └── api.js
├── lib/             # 库文件
│   └── utils.js
└── tests/           # 测试文件
    └── test.js

二、服务配置 #

2.1 manifest.json #

json
{
    "name": "my-service",
    "version": "1.0.0",
    "description": "我的Foxx服务",
    "author": "Your Name",
    "license": "MIT",
    "main": "index.js",
    "configuration": {
        "jwtSecret": {
            "description": "JWT密钥",
            "type": "string",
            "default": "secret"
        }
    },
    "dependencies": {},
    "provides": {},
    "tests": ["tests/*.js"]
}

2.2 配置选项 #

字段 说明
name 服务名称
version 版本号
main 入口文件
configuration 配置参数
dependencies 依赖服务

三、路由定义 #

3.1 基本路由 #

javascript
'use strict';
const createRouter = require('@arangodb/foxx/router');
const router = createRouter();

module.context.use(router);

router.get('/hello', function (req, res) {
    res.send('Hello, World!');
});

3.2 带参数的路由 #

javascript
router.get('/users/:id', function (req, res) {
    const db = require('@arangodb').db;
    const user = db.users.document(req.pathParams.id);
    res.send(user);
});

3.3 POST请求 #

javascript
router.post('/users', function (req, res) {
    const db = require('@arangodb').db;
    const user = db.users.insert(req.body);
    res.send(user);
});

3.4 多种HTTP方法 #

javascript
router.get('/users', listUsers)
    .post('/users', createUser);

router.get('/users/:id', getUser)
    .put('/users/:id', updateUser)
    .delete('/users/:id', deleteUser);

四、请求处理 #

4.1 请求参数 #

javascript
router.get('/search', function (req, res) {
    const keyword = req.queryParams.keyword;
    const page = req.queryParams.page || 1;
    const size = req.queryParams.size || 10;
    
    res.send({
        keyword: keyword,
        page: page,
        size: size
    });
});

4.2 请求体 #

javascript
router.post('/users', function (req, res) {
    const body = req.body;
    res.send(body);
});

4.3 路径参数 #

javascript
router.get('/users/:userId/posts/:postId', function (req, res) {
    const userId = req.pathParams.userId;
    const postId = req.pathParams.postId;
    
    res.send({
        userId: userId,
        postId: postId
    });
});

4.4 请求头 #

javascript
router.get('/info', function (req, res) {
    const auth = req.headers['authorization'];
    const contentType = req.headers['content-type'];
    
    res.send({
        authorization: auth,
        contentType: contentType
    });
});

五、响应处理 #

5.1 发送JSON #

javascript
router.get('/user', function (req, res) {
    res.send({
        name: '张三',
        email: 'zhangsan@example.com'
    });
});

5.2 设置状态码 #

javascript
router.post('/users', function (req, res) {
    res.status(201);
    res.send({
        message: '用户创建成功'
    });
});

5.3 错误响应 #

javascript
router.get('/users/:id', function (req, res) {
    const db = require('@arangodb').db;
    
    try {
        const user = db.users.document(req.pathParams.id);
        res.send(user);
    } catch (e) {
        res.status(404);
        res.send({
            error: '用户不存在'
        });
    }
});

5.4 设置响应头 #

javascript
router.get('/download', function (req, res) {
    res.set('Content-Type', 'application/octet-stream');
    res.set('Content-Disposition', 'attachment; filename="data.json"');
    res.send({ data: 'file content' });
});

六、数据库操作 #

6.1 查询数据 #

javascript
router.get('/users', function (req, res) {
    const db = require('@arangodb').db;
    const users = db._query(`
        FOR user IN users
            RETURN user
    `).toArray();
    
    res.send(users);
});

6.2 带参数查询 #

javascript
router.get('/users/search', function (req, res) {
    const db = require('@arangodb').db;
    const keyword = req.queryParams.keyword;
    
    const users = db._query(`
        FOR user IN users
            FILTER CONTAINS(LOWER(user.name), LOWER(@keyword))
            RETURN user
    `, { keyword: keyword }).toArray();
    
    res.send(users);
});

6.3 插入数据 #

javascript
router.post('/users', function (req, res) {
    const db = require('@arangodb').db;
    
    const user = db.users.insert({
        name: req.body.name,
        email: req.body.email,
        createdAt: new Date().toISOString()
    });
    
    res.status(201);
    res.send(user);
});

6.4 更新数据 #

javascript
router.put('/users/:id', function (req, res) {
    const db = require('@arangodb').db;
    
    const user = db.users.update(req.pathParams.id, req.body);
    res.send(user);
});

6.5 删除数据 #

javascript
router.delete('/users/:id', function (req, res) {
    const db = require('@arangodb').db;
    
    db.users.remove(req.pathParams.id);
    res.status(204);
    res.send();
});

七、认证授权 #

7.1 JWT认证 #

javascript
const jwt = require('@arangodb/foxx/jwt');
const createRouter = require('@arangodb/foxx/router');
const router = createRouter();

router.post('/login', function (req, res) {
    const db = require('@arangodb').db;
    const { username, password } = req.body;
    
    const user = db._query(`
        FOR user IN users
            FILTER user.username == @username
            AND user.password == @password
            RETURN user
    `, { username, password }).toArray()[0];
    
    if (!user) {
        res.status(401);
        res.send({ error: '用户名或密码错误' });
        return;
    }
    
    const token = jwt.sign(
        { userId: user._key },
        module.context.configuration.jwtSecret,
        { expiresIn: '24h' }
    );
    
    res.send({ token: token });
});

module.context.use(router);

7.2 认证中间件 #

javascript
function authMiddleware(req, res, next) {
    const authHeader = req.headers['authorization'];
    
    if (!authHeader) {
        res.status(401);
        res.send({ error: '未提供认证信息' });
        return;
    }
    
    const token = authHeader.replace('Bearer ', '');
    
    try {
        const decoded = jwt.verify(
            token,
            module.context.configuration.jwtSecret
        );
        req.user = decoded;
        next();
    } catch (e) {
        res.status(401);
        res.send({ error: '无效的令牌' });
    }
}

router.get('/profile', authMiddleware, function (req, res) {
    const db = require('@arangodb').db;
    const user = db.users.document(req.user.userId);
    res.send(user);
});

八、服务部署 #

8.1 安装服务 #

Web界面安装:

  1. 打开ArangoDB Web UI
  2. 进入 “Services” 页面
  3. 点击 “Add Service”
  4. 上传服务包或输入Git URL

命令行安装:

bash
foxx install /my-service /path/to/service

8.2 更新服务 #

bash
foxx upgrade /my-service /path/to/service

8.3 卸载服务 #

bash
foxx uninstall /my-service

8.4 查看服务 #

bash
foxx list

九、完整示例 #

9.1 用户API服务 #

manifest.json:

json
{
    "name": "user-api",
    "version": "1.0.0",
    "main": "index.js",
    "configuration": {
        "jwtSecret": {
            "type": "string",
            "default": "my-secret-key"
        }
    }
}

index.js:

javascript
'use strict';
const createRouter = require('@arangodb/foxx/router');
const router = createRouter();
const db = require('@arangodb').db;
const joi = require('joi');

module.context.use(router);

router.get('/users', function (req, res) {
    const page = parseInt(req.queryParams.page) || 1;
    const size = parseInt(req.queryParams.size) || 10;
    
    const users = db._query(`
        FOR user IN users
            SORT user.createdAt DESC
            LIMIT @offset, @limit
            RETURN {
                id: user._key,
                name: user.name,
                email: user.email,
                createdAt: user.createdAt
            }
    `, {
        offset: (page - 1) * size,
        limit: size
    }).toArray();
    
    const total = db._query(`
        FOR user IN users
            COLLECT WITH COUNT INTO count
            RETURN count
    `).toArray()[0];
    
    res.send({
        data: users,
        pagination: {
            page: page,
            size: size,
            total: total
        }
    });
})
.queryParam('page', joi.number().optional())
.queryParam('size', joi.number().optional());

router.post('/users', function (req, res) {
    const user = db.users.insert({
        name: req.body.name,
        email: req.body.email,
        createdAt: new Date().toISOString()
    });
    
    res.status(201);
    res.send({
        id: user._key,
        name: req.body.name,
        email: req.body.email
    });
})
.body(joi.object({
    name: joi.string().required(),
    email: joi.string().email().required()
}).required());

router.get('/users/:id', function (req, res) {
    try {
        const user = db.users.document(req.pathParams.id);
        res.send({
            id: user._key,
            name: user.name,
            email: user.email
        });
    } catch (e) {
        res.status(404);
        res.send({ error: '用户不存在' });
    }
});

router.put('/users/:id', function (req, res) {
    try {
        db.users.update(req.pathParams.id, req.body);
        const user = db.users.document(req.pathParams.id);
        res.send({
            id: user._key,
            name: user.name,
            email: user.email
        });
    } catch (e) {
        res.status(404);
        res.send({ error: '用户不存在' });
    }
});

router.delete('/users/:id', function (req, res) {
    try {
        db.users.remove(req.pathParams.id);
        res.status(204);
        res.send();
    } catch (e) {
        res.status(404);
        res.send({ error: '用户不存在' });
    }
});

十、总结 #

Foxx微服务要点:

  1. 服务结构:manifest.json + index.js
  2. 路由定义:router.get/post/put/delete
  3. 请求处理:req.queryParams、req.body、req.pathParams
  4. 响应处理:res.send()、res.status()
  5. 数据库操作:db._query()、db.collection.method()

下一步,让我们学习集群架构!

最后更新:2026-03-27