项目作者: pmu-tech

项目描述 :
Stub server for REST APIs
高级语言: TypeScript
项目地址: git://github.com/pmu-tech/stub-server.git
创建时间: 2020-01-21T13:23:14Z
项目社区:https://github.com/pmu-tech/stub-server

开源协议:MIT License

下载


@pmu-tech/stub-server

@pmu-tech/stub-server">npm version
Node.js CI
@pmu-tech/stub-server"">Bundle size
Prettier
Airbnb Code Style

Stub/mock server for REST APIs

For each route, decide what will happen: a JSON stub, a piece of JS or redirect to a real server

  • Use it with Express, webpack-dev-server, Next.js or the command line
  • Supports stubs written in JSON, JS, TypeScript, HTML, jpg…
  • Can redirect requests to another host thx to node-http-proxy
  • No need to restart stub-server if you modify a stub
  • The HTTP status code returned is determined from the stub filename: *_200_*.json, *_500_*.html…
  • Configurable delays to simulate slow APIs (helps find bugs and possible improvements to your app - add a spinner, disable a submit button…)
  • Access to request & response objects (allows to change headers, request URL…)

Usage

npm install --save-dev @pmu-tech/stub-server

Proposed file organization

  1. stubs/routes/my_api1_GET_200_OK.json
  2. my_api2_GET_200_OK.jpg
  3. my_api3_POST_400_BadRequest-invalidField.ts
  4. my_api4_POST_400_BadRequest-invalidField.js
  5. my_api5_DELETE_500_InternalServerError.html
  6. my_api7_GET_200_OK.json
  7. my_api7_POST_200_OK.json
  8. my_api8_GET.ts
  9. my_api9_GET.js
  10. my_api10_GET_200_OK-param1.json
  11. my_api10_GET_200_OK-param2.json
  12. stubs/config.ts
  13. webpack.config.ts

stubs/config.ts

  1. import path from 'path';
  2. import { StubServerConfig } from '@pmu-tech/stub-server';
  3. const prod = 'https://myapp.com';
  4. const stubsPath = path.resolve(__dirname, 'routes');
  5. const config: StubServerConfig = {
  6. delay: { min: 500, max: 3000 },
  7. routes: {
  8. '/my/api1': { GET: `${stubsPath}/my_api1_GET_200_OK.json` },
  9. '/my/api2': { GET: `${stubsPath}/my_api2_GET_200_OK.jpg` },
  10. '/my/api3': { POST: `${stubsPath}/my_api3_POST_400_BadRequest-invalidField.ts` },
  11. '/my/api4': { POST: `${stubsPath}/my_api4_POST_400_BadRequest-invalidField.js` },
  12. '/my/api5': { DELETE: `${stubsPath}/my_api5_DELETE_500_InternalServerError.html` },
  13. '/my/api6/:id': {
  14. // Here stub-server works as a proxy,
  15. // example of request sent: PUT https://myapp.com/my/api6/132379
  16. PUT: prod
  17. },
  18. '/my/api7': {
  19. delay: { min: 1000, max: 1000 },
  20. GET: `${stubsPath}/my_api7_GET_200_OK.json`,
  21. POST: {
  22. delay: { min: 0, max: 0 },
  23. response: req => {
  24. req.headers.origin = prod;
  25. return `${stubsPath}/my_api7_POST_200_OK.json`;
  26. }
  27. }
  28. },
  29. '/my/api8': { GET: `${stubsPath}/my_api8_GET.ts`},
  30. '/my/api9': { GET: `${stubsPath}/my_api9_GET.js`},
  31. '/my/api10/:id': { GET: req => `${stubsPath}/my_api10_GET_200_OK-${req.params.id}.json` }
  32. }
  33. };
  34. const rootApiPath = 'https://myapp.com/client/:clientApi';
  35. config.routes[`${rootApiPath}/my/api7`] = { GET: `${stubsPath}/my_api7_GET_200_OK.json` };
  36. export default config; // Or "exports.default = config"

Note: stubs/config.ts written in TypeScript instead of JavaScript requires ts-node

webpack.config.ts

Configuration with webpack-dev-server

  1. import { stubServer } from '@pmu-tech/stub-server';
  2. ...
  3. devServer: {
  4. // webpack 5
  5. onBeforeSetupMiddleware: ({ app }) => {
  6. const configPath = path.resolve(__dirname, 'stubs/config');
  7. stubServer(configPath, app);
  8. }
  9. // webpack 4
  10. before: app => {
  11. const configPath = path.resolve(__dirname, 'stubs/config');
  12. stubServer(configPath, app);
  13. }
  14. ...
  15. },

stubs/routes/my_api1_GET_200_OK.json

  1. {
  2. "foo": "bar"
  3. }

stubs/routes/my_api3_POST_400_BadRequest-invalidField.ts

  1. export default {
  2. error: 'Invalid field'
  3. };

stubs/routes/my_api4_POST_400_BadRequest-invalidField.js

  1. module.exports = {
  2. error: 'Invalid field'
  3. };

stubs/routes/my_api8_GET.ts

  1. import express from 'express';
  2. export default function stub(req: express.Request, res: express.Response) {
  3. res.send('Hello, World!');
  4. }

stubs/routes/my_api9_GET.js

  1. module.exports = (req, res) => {
  2. res.send('Hello, World!');
  3. };

Command line

  1. Usage: stub-server [options] <config>
  2. Arguments:
  3. config path to the config file
  4. Options:
  5. --port <port> stub server port (default: "12345")
  6. --no-delay ignore any delay specified in the config
  7. -h, --help display help for command

Next.js

  1. // stubServer.js
  2. const { stubServer } = require('@pmu-tech/stub-server');
  3. const cors = require('cors');
  4. const express = require('express');
  5. const next = require('next');
  6. const Log = require('next/dist/build/output/log');
  7. const path = require('path');
  8. const app = next({ dev: process.env.NODE_ENV !== 'production' });
  9. const nextjsHandler = app.getRequestHandler();
  10. const configPath = path.resolve(__dirname, 'stubs/config');
  11. const port = 3000;
  12. app.prepare().then(() => {
  13. const server = express();
  14. server.use(express.json());
  15. server.use(cors());
  16. stubServer(configPath, server);
  17. // Next.js only processes GET requests unless you are using [API Routes](https://nextjs.org/docs/api-routes/introduction)
  18. server.get('*', (req, res) => nextjsHandler(req, res));
  19. server.all('*', req => {
  20. throw new Error(`'${req.method} ${req.url}' should be declared in stubs/config.ts`);
  21. });
  22. server.listen(port, () => {
  23. Log.ready(`ready on port ${port}`);
  24. });
  25. });
  1. // package.json
  2. "scripts": {
  3. "dev": "node stubServer.js", // Instead of "next dev"
  4. "build": "next build",
  5. "start": "next start"
  6. ...
  7. },

How to configure stub-server both for stubs and as a proxy

To “connect” your app to an environment (prod, staging, integration…), you can use stub-server as a proxy (intermediary between your app requesting a resource and the server providing that resource).

Why? To configure delays for the HTTP requests (helps find bugs and possible improvements to your app), access to request & response objects…

  1. // package.json
  2. "scripts": {
  3. "start": "...",
  4. "start:staging": "ENVIRONMENT_NAME=staging npm run start",
  5. "start:prod": "ENVIRONMENT_NAME=prod npm run start"
  6. ...
  7. },
  1. // stubs/config.ts
  2. import path from 'path';
  3. import express from 'express';
  4. import { GetStubFilenameOrUrl, StubServerConfig } from '@pmu-tech/stub-server';
  5. const stubsPath = path.resolve(__dirname, 'routes');
  6. type Environments = { [name: string]: { target: string; origin: string } | undefined };
  7. const environments: Environments = {
  8. staging: {
  9. target: 'https://api.staging.myapp.com',
  10. origin: 'https://staging.myapp.com'
  11. },
  12. prod: {
  13. target: 'https://api.myapp.com',
  14. origin: 'https://myapp.com'
  15. }
  16. };
  17. const { target, origin } =
  18. environments[process.env.ENVIRONMENT_NAME ?? 'No environment specified'] ?? {};
  19. function configureRequest(stubOrUrl: string): GetStubFilenameOrUrl {
  20. return (req: express.Request) => {
  21. // You can rewrite the request URL if needed
  22. req.url = req.url.replace('/prefix', '');
  23. if (origin !== undefined) {
  24. // May be required for some APIs
  25. req.headers.origin = origin;
  26. }
  27. return stubOrUrl;
  28. };
  29. }
  30. const config: StubServerConfig = {
  31. delay: { min: 500, max: 3000 },
  32. routes: {
  33. '/prefix/my/api1': {
  34. GET: configureRequest(
  35. // Here stub-server works as a proxy,
  36. // example of request sent: GET https://api.staging.myapp.com/my/api1
  37. target ??
  38. // ...or use a stub if no target specified
  39. `${stubsPath}/my_api1_GET_200_OK.json`
  40. )
  41. },
  42. ...
  43. }
  44. };
  45. export default config;

Output generated

Examples of output (stdout) for requests processed by stub-server:

  1. GET /account/payment-types?imagesWithUrl=true => stubs/routes/payment-types_200_OK.ts, delay: 442 ms
  2. GET /account/captcha/1662973203224 => https://api.myapp.com, delay: 0 ms
  3. GET /account/captcha/1663061235576 => stubs/routes/captcha_200_OK-242674.jpg, delay: 4 ms
  4. POST /account/create => stubs/routes/account_create_201_Created-no-appro.json, delay: 169 ms
  5. POST /auth/session?withNotification=true => https://api.myapp.com, delay: 34 ms

Errors

If the stub is missing or the target is unknown, stub-server returns a 500 (Internal Server Error) response with the error in HTML format and also displays the error in the console.

Examples of errors in the console:

  • ENOENT: no such file or directory, open 'stubs/routes/captcha_200_OK-242674.jpg'
  • Error: getaddrinfo ENOTFOUND api.myapp.com

If you request an unknown route, stub-server does not process it and therefore won’t display anything in the console. It’s up to you to handle this case with a catch-all route:

  1. function missingStubHandler(req: express.Request) {
  2. throw new Error(`Missing stub for '${req.method} ${req.url}'`);
  3. }
  4. ...
  5. '/prefix/*': {
  6. GET: missingStubHandler,
  7. POST: missingStubHandler,
  8. PUT: missingStubHandler,
  9. PATCH: missingStubHandler,
  10. DELETE: missingStubHandler
  11. }