GithubHelp home page GithubHelp logo

swagger2-koa's Introduction

swagger2-koa

Koa 2 async-style middleware for loading, parsing and validating requests via swagger2.

  • router(document) => koa2-style Router
  • validate(document) => koa2 middleware

Installation

$ npm add swagger2-koa

Usage

router(document) => koa2-style Router

This is the easiest way to use swagger2-koa; it creates a standalone koa server, adds the validate middleware, and returns a Router object that allows you to add your route implementations.

import * as swagger from 'swagger2';
import {router as swaggerRouter, Router} from 'swagger2-koa';

...
const document = swagger.loadDocumentSync('./swagger.yml');
const router: Router = swaggerRouter(document);

router.get('/ping', async (context) => {
  context.status = 200;
  context.body = {
    serverTime: new Date().toISOString()
  };
});

...

router.app()         // get the koa 2 server
  .listen(3000);     // start handling requests on port 3000

Note: in addition to validate (described below), router adds the following middleware to its koa server:

  • @koa/cors
  • @koa/router
  • koa-bodyparser

validate(document) => koa2 middleware

If you already have a Koa server, this middleware adds basic loading and validation of HTTP requests and responses against swagger 2.0 document:

import * as swagger from 'swagger2';
import { validate } from 'swagger2-koa';

const app = new Koa();

// load YAML swagger file
const document = swagger.loadDocumentSync('./swagger.yml');

// validate document
if (!swagger.validateDocument(document)) {
  throw Error(`./swagger.yml does not conform to the Swagger 2.0 schema`);
}

app.use(body());
app.use(validate(document));

The validate middleware behaves as follows:

  • expects context.body to contain request body in object form (e.g. via use of koa-body)
  • if the request is for a path not defined in swagger document, an HTTP 404 is returned to the client (subsequent middleware is never processed).
  • if the request body does not validate, an HTTP 400 is returned to the client (subsequent middleware is never processed)
  • if the response body does not validate, an HTTP 500 is returned to the client

For either request (HTTP 400) or response (HTTP 500) errors, details of the schema validation error are passed back in the body. e.g.:

{
  "code": "SWAGGER_RESPONSE_VALIDATION_FAILED",
  "errors": [{
     "actual": {"badTime": "mock"},
     "expected": {
        "schema": {"type": "object", "required": ["time"], "properties": {"time": {"type": "string", "format": "date-time"}}}
     },
     "where": "response"
  }]
}

Debugging

This library uses debug, which can be activated using the DEBUG environment variable:

export DEBUG=swagger2-koa:*

Limitations

  • only supports Koa 2-style async/await middleware interface
  • requires node version 16 and above

License

MIT

swagger2-koa's People

Contributors

adcreare avatar brad-jones avatar carlansley avatar dependabot[bot] avatar jeongukjae avatar ttiurani avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swagger2-koa's Issues

Schema validation should be reworked/optional

I have problems with validation and offering to make it optional via parameter at least.

The schema I'm checking is valid, but I did a test and validated like swagger2:

const fs = require('fs');
const jsonValidator = require('is-my-json-valid');
const schemaValidator = jsonValidator(
  JSON.parse(fs.readFileSync(__dirname + '/node_modules/swagger2/src/schema.json', 'utf8')),
  {
    verbose: true
  }
);

And get error:

Errors [ { field: 'data.paths',
    message: 'referenced schema does not match',
    value: 
     { '/login': [Object],
       '/logout': [Object],

       ...

       '/data': [Object] },
    type: undefined } ]

Schema is actually a valid 2.0 schema and works fine. Also passes swagger-tools validate without a notice.

file parameter deleted by validation

I am have json response that have field 'file'.
{
"1": {
"action": "music",
"action_data": {
"text": "song1",
"file": "/music/245506953044"
},
"title": "song_1",
}
}

After I apply validation I got:
{
"1": {
"action": "music",
"action_data": {
"text": "song1",
},
"title": "song_1",
}
}
Without 1.action_data.file

any suggestion?

Validate options to not check response

Hi, I'm using swagger2-koa on a big Production API and I often get unexpected responses by external APIs integrated by other Devs and I'd like to avoid validating responses, only requests. What do you think about?

Validating header in validate.ts

Hi just wonder in validate.ts.
const validationErrors = swagger.validateRequest(compiledPath, context.method, context.request.query, context.request.body)

Could the library add context.request.header at the end? Now it complains the request schema not match if I mark a header parameter as compulsory.

UpperCase header validation failes

I'm having the following parameter specified in my Swagger spec:

  • name: X-Test-Version
    in: header
    type: string
    required: true
    default: 1
    When I'm validating the request it returns:
    {
    "code": "SWAGGER_REQUEST_VALIDATION_FAILED",
    "errors": [
    {
    "expected": {
    "type": "string"
    },
    "where": "header"
    }
    ]
    }

Printing out some statements in validate.ts gives me:
for headers:
{......
'x-test-version': '1'
....}
for headers[parameter.name]:
X-Test-Version

Code of validate.ts:
case 'header':
value = (headers || {})[parameter.name];
break;

So it is trying to get the value via headers[X-Test-Version], which cannot be found, so value is undefined.
The following would solve the issue:
case 'header':
value = (headers || {})[(parameter.name).toLowerCase()];
break;

How to prefix routes?

is there a way to define a prefix for routes?

eg I have everything under a /user route and all the items I define under that route would prefix with /user.

Also, multiple router / prefixes , eg /user, /posts?

Exporting routes from a controller

Hi,
I'm looking to implement this module for a project, but I'm not sure how I would go about splitting up my project in separate controllers.
Normally I would export a router from the controller and use the routes().

Is this possible somehow with the swaggerRouter?

Thanks!

koa logging middleware does not log requests on swagger api routes

I am using koa-pino-logger as a middleware logger that logs request and adds logger to context there are two issues:

  1. request to swagger api routes are not being logged (but the swagger document routes are)
  2. the logger is not on the context inside the swagger api routes

I added the logging middleware as follows:

r.app()
  .use(logger())
  .use(bodyParser())
  .use(ui(document))
  .listen(3000)

And I get the following log output (piped through pino-colada) when hittting the swagger docs and the api points:

08:35:23 ✨ request completed GET 200 /api-docs 4ms
08:35:23 🚨 request errored GET 404 /favicon.ico
08:35:23 ✨ request completed GET 404 /favicon.ico 3ms
08:35:28 ✨ request completed GET 200 /
08:35:28 ✨ request completed GET 200 /swagger-ui.css 6ms
08:35:28 ✨ request completed GET 200 /swagger-ui-standalone-preset.js 6ms
08:35:28 ✨ request completed GET 200 /swagger-ui-bundle.js 9ms

(no api calls)

if I try to call the logger in the following api route:

r.get('/health', async ctx => {
  ctx.log.info('log something')
  ctx.status = 200
  ctx.body = {
    status: 'ok'
  }
})

I get the following error:

  TypeError: Cannot read property 'info' of undefined
      at /Users/chris/work/node/topic-service/index.js:14:11
      at dispatch (/Users/chris/work/node/topic-service/node_modules/koa-router/node_modules/koa-compose/index.js:44:32)
      at next (/Users/chris/work/node/topic-service/node_modules/koa-router/node_modules/koa-compose/index.js:45:18)
      at /Users/chris/work/node/topic-service/node_modules/koa-router/lib/router.js:346:16
      at dispatch (/Users/chris/work/node/topic-service/node_modules/koa-router/node_modules/koa-compose/index.js:44:32)
      at /Users/chris/work/node/topic-service/node_modules/koa-router/node_modules/koa-compose/index.js:36:12
      at dispatch (/Users/chris/work/node/topic-service/node_modules/koa-router/lib/router.js:351:31)
      at dispatch (/Users/chris/work/node/topic-service/node_modules/koa-compose/index.js:42:32)
      at /Users/chris/work/node/topic-service/node_modules/swagger2-koa/dist/validate.js:66:15
      at dispatch (/Users/chris/work/node/topic-service/node_modules/koa-compose/index.js:42:32)

Simple GET request validation does not work

I always get error like this with no clear explanation what is wrong.

{"code":"SWAGGER_RESPONSE_VALIDATION_FAILED","errors":[{"actual":{},"where":"response"}]}

Here is my sample app

import Koa from "koa";
import Router from "koa-router";
import bodyParser from "koa-bodyparser";
import * as swagger from 'swagger2';
import { validate, ui } from 'swagger2-koa';

export const app = new Koa();
export const api = Router();
const swaggerSpec = swagger.loadDocumentSync(__dirname + '/api.yml');

if (!swagger.validateDocument(swaggerSpec)) {
  throw new Error(`api.yml does not conform to the Swagger 2.0 schema`);
}

api.get("/admin/client", async (ctx)=>{
	console.log("GET /admin/client", ctx.params, "1 ", ctx.body, " 2");	 
	ctx.response.body = ctx.body = {
		next: 1234567890,
		clients: []
	};
	ctx.status = 200;
});

app	
	.use(bodyParser())
	.use(ui(swaggerSpec, "/swagger"))
	.use(validate(swaggerSpec))	
	.use(api.routes());

And this is relevant piece of my Swagger

paths:
  "/admin/client":
    get:
      description: |
        Get list of available clients. If you specify 'q' parameter then full text search is performed 
      parameters:
      - name: q
        in: query
        description: "Lucene query string"
        type: string
      responses:
        200:
          description: OK
          examples:
            application/json:
              next: 1234567890
              clients:
              - email: [email protected]
                facebookId: null
        401:
          $ref: '#/responses/NotAuth'

Document not exposed after using swaggerRouter

As on tin - the example provided in the readme for the "UI" middleware (which looks like it'd be nice to be able to use) is sadly not possible as "document" itself is not directly exposed. We'd have to resort to calling swagger.loadDocumentSync ourselves, which honestly should not be necessary.

Replace koa-router with custom Swagger-aware router

Specifically, we need a better router than can populate the context.params object with all the various swagger-defined parameters (from the path, header, query, formData or body), and map them to the correct types (e.g. to an actual array from a CSV formatted string).

Templated parameters fail on request check

Here is my swagger file:

swagger: '2.0'
info:
  title: Test API
  description: Test API
  version: 1.0.0
  contact:
    email: [email protected]
schemes:
  - http
  - https
paths:
  /debug/ping:
    get:
      tags:
        - debug
      summary: ping
      description: returns a pong
      operationId: ping
      responses:
        '200':
          description: successful operation
          schema:
            $ref: '#/definitions/PingSuccessResponse'
        default:
          description: any error
          schema:
            $ref: '#/definitions/FailureResponse'
  '/marketers/{id}':
    get:
      consumes:
        - application/json
      produces:
        - application/json
      tags:
        - marketers
      summary: returns a marketer
      description: returns a marketer specified by id
      parameters:
        - name: id
          in: path
          type: integer
          minimum: 1
          required: true
          description: marketer id
      responses:
        '200':
          description: success
          schema:
            $ref: '#/definitions/SuccessResponse'
        default:
          description: any error
          schema:
            $ref: '#/definitions/FailureResponse'
definitions:
  SuccessResponse: &ref_0
    type: object
    required:
      - code
      - payload
    properties:
      code:
        type: number
        enum:
          - 200
      payload:
        type: object
  FailureResponse:
    type: object
    required:
      - code
      - error
    properties:
      code:
        type: number
      error:
        type: object
        required:
          - message
        properties:
          message:
            type: string
          extended:
            type: object
  PingSuccessResponse:
    allOf:
      - *ref_0
      - properties:
          payload:
            type: object
            properties:
              status:
                type: string

When trying to access http://localhost:3000/marketers/1 the validation does not pass:

{"code":"SWAGGER_REQUEST_VALIDATION_FAILED","errors":[{"actual":"{id}","expected":{"type":"integer"},"where":"path"}]}}}

I can "solve" the problem with setting parameter type to string:

parameters:
        - name: id
          in: path
          type: string
          minimum: 1
          required: true
          description: marketer id

which defeats the purpose of swagger validation.

What do I do wrong?

System:

swagger2-koa: 0.0.40
node: 9.4.0

Support for external $ref

I'm not sure if this is a limitation of the library or not, but I'm running into an issue using external $ref. For example I have a swagger doc defined as

swagger: '2.0'
info:
  title: My API
  version: '1.0'
  description: 'Its cool.'
host: localhost
schemes:
  - https
produces:
  - application/json
consumes:
  - application/json
paths:
  /test:
    post:
      operationId: testPost
      summary: post
      tags:
        - post
      description: Post
      produces:
        - application/json
      parameters:
        - in: body
          name: theBody
          description: toCreate
          required: true
          schema:
            $ref: './commonModels.json#/coolModel'
      responses:
        '200':
          schema:
            type: object

where commonModels.json looks something like this:

coolModel:
  description: sweet
  type: object
  properties:
    code:
      type: integer
    type:
      type: string
    message:
      type: string

My understanding for the swagger 2.0 spec is that should work, is this implemented in swagger2-koa?

Response models validate but do not appear in api docs

Given the following syntax from api.yaml, request validation works fine. However, the response model shows up as "string".
If I remove "shema" from the put responses object, then the api docs show the model correctly but then response validation fails. How can I achieve both?

/tenant/{tenantId}:
  put:
      ...
      responses:
            200:
              description: "OK"
              schema:     <-- keeping this here works for validation but not displaying the model
                $ref: '#/responses/Tenant'
responses:
  "Tenant":
    schema:
      $ref: '#/definitions/Tenant'
definitions:
  Tenant:
    type: "object"
    properties:
      status: 
        type: "string"
      data:
        type: "object"
        properties:
          id:
            type: "integer"
            format: "int64"
          firstName:
            type: "string"
          lastName:
            type: "string"
          email:
            type: "string"
          createdAt:
            type: "string"
          updatedAt:
            type: "string"
          deletedAt:
            type: "string"

Get Routes Not Found

When using this module, my Get routes no longer work. Put, Delete, Post all keep working. If I comment out the swagger code, Get routes start working again. Why is this happening?

I get "404 Not Found" on the Get routes when swagger is on.

server.js

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const combineRouters = require('koa-combine-routers');
const redisRouter = require('./routes/redis');

const app = new Koa();

const router = combineRouters([
  redisRouter
])

// SWAGGER SETUP
const swagger = require('swagger2');
const swaggerKoa = require('swagger2-koa');
const document = swagger.loadDocumentSync(__dirname + '/swagger/api.yml');
app.use(swaggerKoa.ui(document));

app.use(router);

app.listen(8080);

module.exports = app;

routes/redis.js

const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const router = new Router({ prefix: '/api/redis' });
const redis = require('../functions/redis');

router.use(bodyParser());

router.get('/:reportObj', async (ctx, next) => {
	ctx.body = await redis.getData(ctx.params.reportObj, '.');
});

router.get('/:reportObj/:rootPos', async (ctx, next) => {
	ctx.body = await redis.getData(ctx.params.rootPos, ctx.params.rootPos);
});

router.post('/:reportObj', async (ctx, next) => {
	ctx.body = await redis.setData(ctx.params.reportObj, ctx.request.body);
});

router.put('/:reportObj/:rootPos', async (ctx, next) => {
	ctx.body = await redis.appendData(ctx.params.reportObj, ctx.params.rootPos, ctx.request.body);
});

router.delete('/:reportObj', async (ctx, next) => {
	ctx.body = await redis.deleteData(ctx.params.reportObj, '.');
});

module.exports = router;

number inputs for numeric path parameters not being considered valid

As on tin.

Requesting the following...

http://localhost:9990/api/v1.0/post/1

...which is defined as...

swagger: "2.0"
info:
  version: "1.0"
  title: "thing"
basePath: "/api"
schemes:
- "http"
paths:
  /v1.0/post/{id}:
    get:
      tags:
      - "post"
      summary: "Gets a post"
      description: ""
      operationId: "getPost"
      produces:
      - "application/json"
      parameters:
      - name: "id"
        in: "path"
        # FIXME: https://github.com/carlansley/swagger2-koa/issues/20
        type: "integer"
        format: "int64"
        # type: "string"
        description: "The post to fetch."
        required: true
      responses:
        200:
          description: "successful operation"
          schema:
            type: "string"

This is currently failing with the following:

{"code":"SWAGGER_REQUEST_VALIDATION_FAILED","errors":[{"actual":"{id}","expected":{"type":"number"},"error":"data is the wrong type","where":"path"}]}

...Which is pretty far from the case, since {id} is a numeric input in this case.

Changing the type to "string" in the swagger file results in validation passing, which is not a desirable workaround.

Two issues of the path validation

  1. Beginning Slash Issue
    --> When basePath is set to root, e.g. "/", the path validation will be not found all the time because regex would start from double slash like "//v1/something". It can be solved by simply remove basePath, but it would be great I can have the basePath to be "/" on the document.

  2. End Slash Issue
    --> When path is, for example, "/v1/something", I cannot request for "/v1/something/", and vice versa. It would be great to adapt these two kinds of request format.

Add support for Response headers

The current version does not allow setting/getting response headers. Please add support for the same via Koa set and get functions.

[low prio] ui middleware does not bind properly to some paths.

I have the following setup:

app.use(cors());
app.use(koaBody());
app.use(validate(document));

router.get('api/path/', ...);
router.post('api/path/', ...);

app.use(router.routes());

app.use(ui(document, '/api'));

app.use(async (ctx) => {
    let urlPath = ctx.path;
    if (ctx.path === '/') urlPath = '/index.html';
    await send(ctx, urlPath, { root: path.resolve(__dirname, '../../frontend') });
});

When I try to access swagger with host/api I get 404 not found.
What is the reason for that (does router bind to this path)?

When I change app.use(ui(document, '/api')); to app.use(ui(document)); and try to access swagger with host/ I get: Can't read swagger JSON from http://localhost:3000/api-docs

So 'host/' resource is received but when it tries to access the api-docs resource it fails with 404 not found. It looks like ui does not bind to that path.

Finally, I managed to make it running by binding ui to some other root path like /swagger.

So the two questions are why cannot I bind to '/' or '/api'?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.