Skip to content

Add MSW plugin for API mocking in testing and development#93

Merged
hotlong merged 4 commits intomainfrom
copilot/add-msn-plugin-support
Jan 24, 2026
Merged

Add MSW plugin for API mocking in testing and development#93
hotlong merged 4 commits intomainfrom
copilot/add-msn-plugin-support

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 24, 2026

Implements Mock Service Worker integration for ObjectStack, enabling browser and Node.js API mocking without server infrastructure.

Implementation

New Package: @objectstack/plugin-msw

  • MSWPlugin - Runtime plugin implementing automatic handler generation for all ObjectStack endpoints
  • ObjectStackServer - Simplified mock API for common CRUD operations
  • Auto-generates handlers for discovery, metadata, data, and UI protocol endpoints

Example Package: @objectstack/example-msw-demo

  • Browser standalone usage pattern
  • Runtime integration pattern

Usage

Browser Mode (standalone):

import { setupWorker } from 'msw/browser';
import { http, HttpResponse } from 'msw';
import { ObjectStackServer } from '@objectstack/plugin-msw';

ObjectStackServer.init(protocol);

const handlers = [
  http.get('/api/user/:id', async ({ params }) => {
    const result = await ObjectStackServer.getUser(params.id);
    return HttpResponse.json(result.data, { status: result.status });
  })
];

export const worker = setupWorker(...handlers);

Runtime Integration:

const kernel = new ObjectStackKernel([
  new MSWPlugin({ baseUrl: '/api/v1', logRequests: true })
]);

Endpoints Mocked

  • /api/v1 - Discovery
  • /api/v1/meta[/:type[/:name]] - Metadata access
  • /api/v1/data/:object[/:id] - CRUD operations (GET, POST, PATCH, DELETE)
  • /api/v1/ui/view/:object - UI protocol
Original prompt

新增插件,支持 msn

// mocks/browser.js
import { setupWorker, rest } from 'msw';
import { ObjectStackServer } from '../server/mock-db';

// 1. 初始化模拟服务端
ObjectStackServer.init();

// 2. 定义路由处理器 (Handlers)
// 这就像是 Express/Koa 的路由定义,但运行在 Service Worker 中
const handlers = [

// 拦截 GET /api/user/:id
rest.get('/api/user/:id', async (req, res, ctx) => {
const { id } = req.params;

// 调用本地逻辑
const result = await ObjectStackServer.getUser(id);

// 返回构建的 Response
return res(
  ctx.status(result.status),
  ctx.json(result.data)
);

}),

// 拦截 POST /api/user
rest.post('/api/user', async (req, res, ctx) => {
const body = await req.json();

// 调用本地逻辑
const result = await ObjectStackServer.createUser(body);

return res(
  ctx.status(result.status),
  ctx.json(result.data)
);

}),
];

// 3. 创建 Worker 实例
export const worker = setupWorker(...handlers);


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
spec Error Error Jan 24, 2026 3:50am

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI and others added 2 commits January 24, 2026 03:43
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add plugin to support MSN Add MSW plugin for API mocking in testing and development Jan 24, 2026
Copilot AI requested a review from hotlong January 24, 2026 03:51
@github-actions github-actions bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tooling size/l labels Jan 24, 2026
@hotlong hotlong marked this pull request as ready for review January 24, 2026 04:09
Copilot AI review requested due to automatic review settings January 24, 2026 04:10
@hotlong hotlong merged commit 5d6009d into main Jan 24, 2026
14 of 15 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Mock Service Worker (MSW) integration to ObjectStack, enabling browser and Node.js API mocking without server infrastructure. The implementation includes a new plugin package and example usage demonstrations.

Changes:

  • New @objectstack/plugin-msw package with MSWPlugin and ObjectStackServer classes for automatic MSW handler generation
  • Example package @objectstack/example-msw-demo demonstrating both browser standalone and runtime integration patterns
  • Automatic endpoint generation for discovery, metadata, data CRUD operations, and UI protocol

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/plugin-msw/package.json Package configuration with MSW 2.0+ dependency and runtime peer dependency
packages/plugin-msw/src/msw-plugin.ts Main plugin implementing RuntimePlugin interface with handler generation logic
packages/plugin-msw/src/index.ts Package exports for MSWPlugin, ObjectStackServer, and MSW types
packages/plugin-msw/tsconfig.json TypeScript configuration for ES2020 modules
packages/plugin-msw/README.md Comprehensive documentation with usage examples and API reference
packages/plugin-msw/CHANGELOG.md Version 0.3.1 initial release notes
examples/msw-demo/package.json Example package dependencies and scripts
examples/msw-demo/src/server.ts Runtime integration pattern example
examples/msw-demo/src/browser.ts Browser standalone usage example
examples/msw-demo/tsconfig.json TypeScript configuration for NodeNext modules
examples/msw-demo/README.md Example usage documentation
pnpm-lock.yaml Lock file updates for new packages and MSW dependencies
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +328 to +334
async onStop() {
if (this.worker) {
this.worker.stop();
console.log(`[MSWPlugin] Stopped MSW worker.`);
}
}

Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onStop lifecycle method is not part of the RuntimePlugin interface. The interface only defines optional install and onStart methods (see packages/runtime/src/types.ts). This method will never be called by the kernel and represents dead code. Consider removing it or extending the RuntimePlugin interface to include an onStop hook if this functionality is needed.

Suggested change
async onStop() {
if (this.worker) {
this.worker.stop();
console.log(`[MSWPlugin] Stopped MSW worker.`);
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +22
const mockProtocol = {
getData: async (object: string, id: string) => {
return { id, object, name: `Mock ${object}`, status: 'active' };
},
createData: async (object: string, data: any) => {
return { id: 'new-id', ...data };
},
// Add other methods as needed
} as any;
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock protocol object uses TypeScript any type casting without proper error handling or type checking. This could lead to runtime errors if the protocol methods are not properly implemented. Consider creating a proper mock that implements the expected protocol interface or using a testing library to create type-safe mocks.

Suggested change
const mockProtocol = {
getData: async (object: string, id: string) => {
return { id, object, name: `Mock ${object}`, status: 'active' };
},
createData: async (object: string, data: any) => {
return { id: 'new-id', ...data };
},
// Add other methods as needed
} as any;
interface MockProtocol {
getData(object: string, id: string): Promise<unknown>;
createData(object: string, data: unknown): Promise<unknown>;
}
const mockProtocol: MockProtocol = {
getData: async (object: string, id: string) => {
return { id, object, name: `Mock ${object}`, status: 'active' };
},
createData: async (object: string, data: unknown) => {
return { id: 'new-id', ...data as Record<string, unknown> };
},
// Add other methods as needed
};

Copilot uses AI. Check for mistakes.
/**
* Custom handlers to add to MSW
*/
customHandlers?: Array<any>;
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Array<any> for handler types weakens type safety. MSW provides specific handler types (e.g., RequestHandler from 'msw') that should be used instead. This would provide better type checking and IDE support for consumers of this API.

Copilot uses AI. Check for mistakes.
name = 'msw';
private options: MSWPluginOptions;
private worker: any;
private handlers: Array<any> = [];
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Array<any> for the handlers property weakens type safety. Consider using a more specific type like RequestHandler[] from MSW to provide better type checking.

Copilot uses AI. Check for mistakes.
export class MSWPlugin implements RuntimePlugin {
name = 'msw';
private options: MSWPluginOptions;
private worker: any;
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using any type for the worker weakens type safety. Consider importing and using the proper type from MSW (e.g., SetupWorker from 'msw/browser') to provide better type checking and IDE support.

Suggested change
private worker: any;
private worker: ReturnType<typeof setupWorker> | null = null;

Copilot uses AI. Check for mistakes.
Comment on lines +241 to +250
try {
const result = await ObjectStackServer.getData(
params.object as string,
params.id as string
);
return HttpResponse.json(result.data, { status: result.status });
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
return HttpResponse.json({ error: message }, { status: 404 });
}
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling pattern here catches errors and returns a 404 status, but the getData method in ObjectStackServer.getData already handles errors and returns a response object with status and data. This creates a double error handling that could mask the original error. The outer try-catch may be unnecessary since ObjectStackServer.getData already handles errors internally.

Suggested change
try {
const result = await ObjectStackServer.getData(
params.object as string,
params.id as string
);
return HttpResponse.json(result.data, { status: result.status });
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
return HttpResponse.json({ error: message }, { status: 404 });
}
const result = await ObjectStackServer.getData(
params.object as string,
params.id as string
);
return HttpResponse.json(result.data, { status: result.status });

Copilot uses AI. Check for mistakes.
* ```
*/
export class MSWPlugin implements RuntimePlugin {
name = 'msw';
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugin name 'msw' is inconsistent with the naming pattern used in other ObjectStack plugins. For example, HonoServerPlugin uses 'com.objectstack.server.hono' (reverse domain notation). Consider using a similar pattern like 'com.objectstack.msw' or 'com.objectstack.plugin.msw' for consistency across the codebase.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants