Giter VIP home page Giter VIP logo

elysia-swagger's People

Contributors

aleclarson avatar arthurfiorette avatar autumnlight02 avatar bogeychan avatar gomah avatar hagishi avatar hrdtr avatar igrschmidt avatar imkylecat avatar jmcudd avatar kaanduraa avatar kkelk avatar leandropereiradacruz avatar marclave avatar masnormen avatar mentos1386 avatar mohankumargupta avatar nxht avatar phaux avatar pragmatta avatar saltyaom avatar sinasab avatar steele123 avatar ydrogen 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

Watchers

 avatar  avatar

elysia-swagger's Issues

High Memory Usage (~100mb)

Just want to note that adding this plugins adds ~100mb of memory usage:

CleanShot 2023-04-27 at 07 10 22
(Left: With Swagger Plugin, Right: Without Swagger Plugin)

CleanShot 2023-04-27 at 07 00 36

Elysia schema creates malformed swagger schema

Ola,

I want to make a nice swagger schema view of my api. While the @elysia/swagger plugin gets there half of the way, it seems to struggle with schemas. I've dug through all the examples, but none seem to mix params and responses together, or the examples are outdated. (Like the readme of this repo). I've tried different ways of creating layouts for the schemas, but non of them seem to work properly.


I've got an Elysia route set up like the following:

import { Elysia, t } from "elysia"
import { song } from "../../interfaces";
import { db } from "../..";

export const getSkip = (app: Elysia) => app
  .get("/skip/:id", ({ params }) => {
    return db.query("SELECT user_id, start, end FROM skip WHERE `song_id`=$songId").all({
      $songId: params.id
    }) as song[];
  }, {
    params: t.Object({
      id: t.Number({
        description: "The ID of the song you want to fetch skips from."
      })
    }, {
      description: "Request body to get all skips belonging to a song."
    }),
    response: {
      200: t.Array(
        t.Object({
          user_id: t.Number({
            description: "The Soundcloud id of the uploader.",
            minimum: 0
          }),
          start: t.Number({
            description: "Timestamp where the song skip starts.",
            minimum: 0
          }),
          end: t.Number({
            description: "Timestamp where the song skip ends.",
            minimum: 0
          })
        }), {
        description: "Returns an array of skip items belonging to the song id."
      }
      ),
    },
    type: ["arrayBuffer", "application/json"],
    description: "Gets all skips of a song.",
  })

Then I run the route like this:

new Elysia()
   .use(swagger(swaggerDocs)) //Basic dcoumentation options
    .group("/skip", app => app
      .use(getSkip) //The route from above
    )
   .listen(port, () => console.log(`Listening on port ${port}...`));

My swagger starts up successfully, but some functionality is broken. I can not inspect the schema, and can not log into my oauth.
image

I threw the generated swagger schema into insomnia to inspect any errors, it came out with the following:

❌ Elysia generated

{
  "openapi": "3.0.3",
  "components": {
    "schemas": {}
  },
  "security": [
    {
      "ApiKeyAuth": [] ❌ <--- Api "security" values must match a scheme defined in the "components.securitySchemes" object
    }
  ],
  "servers": [
    {
      "description": "Local host",
      "url": "http://localhost:3001"
    }
  ],
  "info": {
    "title": "Skipcloud API",
    "description": "API used to upload sections of soundcloud songs to skip over.",
    "version": "0.0.1",
    "contact": {
      "name": "@hang_yourself on discord"
    }
  },
  "paths": {
    "/skip/{id}": {
      "get": {
        "parameters": [
          {
            ❌ <--- "0" property must have required property "schema".
            "description": "The ID of the song you want to fetch skips from.",
            "type": "number",
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Returns an array of skip items belonging to the song id.",
            "items": { ❌ <--- Property "items" is not expected to be here
              "type": "object",
              "properties": {
                "user_id": {
                  "description": "The Soundcloud id of the uploader.",
                  "minimum": 0,
                  "type": "number"
                },
                "start": {
                  "description": "Timestamp where the song skip starts.",
                  "minimum": 0,
                  "type": "number"
                },
                "end": {
                  "description": "Timestamp where the song skip ends.",
                  "minimum": 0,
                  "type": "number"
                }
              },
              "required": [
                "user_id",
                "start",
                "end"
              ]
            },
            "content": {
              "arrayBuffer": {
                "schema": {
                  "type": "array"
                }
              },
              "application/json": {
                "schema": {
                  "type": "array"
                }
              }
            }
          }
        },
        "operationId": "getSkipById"
      }
    }
  }
}

Fixes

✅ Error 1

I have an API key in my header, I have defined my swagger schema as such:

import { ElysiaSwaggerConfig } from "@elysiajs/swagger/src/types";

export default {
  documentation: {
    components: {
      securitySchemes: {
        ApiKeyAuth: {
          type: "apiKey",
          in: "header",
          name: "Authorization",
          description: "Key used to log into the soundcloud API."
        }
      },
     //... etc
   }
} as ElysiaSwaggerConfig;

This however does not show up in the schema. The schema stays empty.

✅ Error 2

The first error seems to be caused by not putting "type: number" in a schema object:

"parameters": [
  {
    "description": "The ID of the song you want to fetch skips from.",
    "schema": {
       "type": "number"
    },
    "in": "path",
    "name": "id",
    "required": true
  }
],

✅ Error 3

This error seems to be caused by the response schema directly being in the 200 object, instead of inside the content objects.
image

"responses": {
  "200": {
    "description": "Returns an array of skip items belonging to the song id.",
    "content": {
      "arrayBuffer": {
        "schema": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "user_id": {
                "description": "The Soundcloud id of the uploader.",
                "minimum": 0,
                "type": "number"
              },
              "start": {
                "description": "Timestamp where the song skip starts.",
                "minimum": 0,
                "type": "number"
              },
              "end": {
                "description": "Timestamp where the song skip ends.",
                "minimum": 0,
                "type": "number"
              }
            },
            "required": [
              "user_id",
              "start",
              "end"
            ]
          }
        }
      },
      "application/json": {
        "schema": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "user_id": {
                "description": "The Soundcloud id of the uploader.",
                "minimum": 0,
                "type": "number"
              },
              "start": {
                "description": "Timestamp where the song skip starts.",
                "minimum": 0,
                "type": "number"
              },
              "end": {
                "description": "Timestamp where the song skip ends.",
                "minimum": 0,
                "type": "number"
              }
            }
          }
        }
      }
    }
  }
}

With these manual fixes, I end up with the schema view that I want:

image

I hope this post clearly outlines the issues I am facing with the swagger plugin. If there is any context missing, please let me know.
Please don't be another stupid oversight by myself🙏

Running into `Schemas with 'type: array', require a sibling 'items: ' field` error

Running into Schemas with 'type: array', require a sibling 'items: ' field error when validating the OpenAPI schema that's generated by the plugin.

// this is my endpoint
  .get("/", ({ store: { db }, orgId }) => getCustomers(db(), orgId), {
    response: t.Array(Customer),
  })
  
// this is the type
import { BaseDbModel } from "./baseTypes";
import { Static } from "@sinclair/typebox";
import { t } from "elysia";

export const CustomerParams = t.Object({
  number: t.Optional(t.Union([t.String(), t.Null()])),
});
export type CustomerParams = Static<typeof CustomerParams>;

export const Customer = t.Intersect([
  BaseDbModel,
  CustomerParams,
  t.Object({ orgId: t.String() }),
]);
export type Customer = Static<typeof Customer>;

Would appreciate any ideas!

Scalar options aren't applied

The scalarConfig object is ignored for the most part, instead of actually applying to the Scalar UI. As a result, almost all these options have no effect if you try to set them:

image

I checked the source code and found that only the customCss and spec.url options are applied.

Is this an oversight, or deliberate?

If I understand the docs correctly, it could be fixed with this:

<script
    id="api-reference"
    data-url="${config.spec?.url}"
    data-configuration="${JSON.stringify(config)}"
></script>

Using:

  • elysia-swagger: 0.8.3
  • elysia: 0.8.9
  • bun: 1.0.20

Invalid parameters schema keywords location

Based on

The schema keywords should go under schema key.

Example code:

import swagger from '@elysiajs/swagger';
import { Elysia, t } from 'elysia';

const app = new Elysia()
  .use(swagger())
  .get('/', async () => 'hi', {
    query: t.Object({
      a: t.Optional(t.String({
        title: 'title',
        enum: ['a', 'b'],
        pattern: 'a|b',
        default: 'b',
        minLength: 1,
        maxLength: 10,
      })),
    }),
  })
  .listen(3000);

console.log(
  `🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}`,
);

Current parameter json

{
  "parameters": [
    {
      "title": "title",
      "enum": [
        "a",
        "b"
      ],
      "pattern": "a|b",
      "default": "b",
      "minLength": 1,
      "maxLength": 10,
      "schema": {
        "type": "string"
      },
      "in": "query",
      "name": "a",
      "required": false
    }
  ]
}

Expected parameter json

{
  "parameters": [
    {
      "schema": {
        "title": "title",
        "enum": [
          "a",
          "b"
        ],
        "pattern": "a|b",
        "default": "b",
        "minLength": 1,
        "maxLength": 10,
        "type": "string"
      },
      "in": "query",
      "name": "a",
      "required": false
    }
  ]
}

Path descriptions

Is somehow possible to include descriptions for the paths in the generated docs, e.g. from docblock/comments?

Support for nullable property in OpenAPI 3

I have model defined like

t.Object({
  numericProp: t.Nullable(t.Number()),
})

This generates expected type TUnion<[TNumber, TNull]>.

And in OpenAPI I'm getting

"numericProp": {
  "anyOf": [
    {
      "type": "null"
    },
    {
      "type": "number"
    }
  ]
}

OpenAPI spec says

OpenAPI 3.0 does not have an explicit null type as in JSON Schema, but you can use nullable: true to specify that the value may be null. Note that null is different from an empty string "".

So, the expected OpenAPI schema would be

"numericProp": {
  "type": "null",
  "nullable": true
}

SwaggerUI also has problem rendering model example, saying "numericProp": "Unknown Type: null",

Property set to never is shown in the docs

In my app, I have the following Elysia/Typebox schema:

const userSchema = t.Object({
  ...,
  password: t.Never(),
})

This prevents the API from sending the password field as part of its response.

But even with that, the password field is shown in the Swagger generated docs. I strongly believe it shouldn't be there.
image

proposal: package name change to elysiajs/openapi

Swagger is version 2.0 of OpenAPI, we should promote the OpenAPI standard :) especially since OpenAPI 4.0 is being released this year!

Wanted to open a discussion about renaming or potentially having a new package release with the OpenAPI name?

Thoughts? 🤔 @SaltyAom

bug: not provided guard json schema to routes

I would like to compose routes and not use multidimensional nesting

import http from 'node:http';
import { createServerAdapter } from '@whatwg-node/server';

import { Elysia, t } from 'elysia';
import { swagger } from '@elysiajs/swagger';

const setup = (app: Elysia) =>
	app.use(
		swagger({
			path: '/docs',
			provider: 'swagger-ui',
		})
	);

// Dont work
const router = new Elysia({
	prefix: '/:accountId',
	name: 'account-router',
}).guard({
	headers: t.Object({
		'x-remote-user': t.String({ title: 'Remote User', description: '...' }),
	}),
});
// Dont provide headers schema
router.get('/', ({ headers }) => {
	return {
		from: headers['x-remote-user'],
	};
});


// Work
const router2 = new Elysia({
	prefix: '/v2/:accountId',
	name: 'account-router',
}).guard(
	{
		headers: t.Object({
			'x-remote-user': t.String({
				title: 'Remote User',
				description: '...',
			}),
		}),
	},
    // Its ok provided
	(app) =>
		app.get('/', ({ headers }) => {
			return {
				from: headers['x-remote-user'],
			};
		})
);

const app = new Elysia().use(setup).use(router).use(router2);

const adapter = createServerAdapter(async (request) => {
	return app.handle(request);
});

http.createServer(adapter).listen(3000);
image image

Response Schema Mismatch Error

> router/account.ts

export const account = new Elysia()
  .group('/account', (app) =>
  app
    .decorate('Login', LoginController)
    .use(jwtAccessSetup)
    .use(jwtRefreshSetup)
    .post('/sendcode', async ({ body, Login }) => {
      return await Login.sendcode(body)
    }, 
    { 
      body: t.Object({
        phone: t.String({ pattern: '^[0-9]{10,16}$', default: '79251234567' }),
      }),
      detail: {
        tags: ['accounts']
      },
      response: {
        200: t.Object({
          data: t.Object({
            code: t.Number({ enum: [1234] }), // ONLY FOR DEV MODE. SHOULD BE DELETED IN PROD
            message: t.String({ enum: ['success'] }),
          })
        }),
        500: t.Object({
          message: t.String({ enum: ['Internal Server Error'] }) // Specify the exact error message
        })
      },
       
    })
> index.ts

const app = new Elysia()
  app
    .use(swagger()
    .use(account)
    .listen(3000);

If you specify response as here click
u will get an error while trying to make responce like this one

изображение

cause when some values in response apeared then elysia or ts (i dont know exactly) try to look not on body validation but on response validation while making request to server.

Expect behavior: when the client makes a request to the server, the validation of the body should be based on the schema that lies in the body, not in the response.

Issue when using prefix in Elysia

When using Elysia like this:

import { Elysia } from "elysia";
import { swagger } from "@elysiajs/swagger";

const app = new Elysia({ prefix: "/api" }).use(
  swagger({
    path: "/swagger",
  })
);

app.get("/", (ctx) => ctx.headers);

app.listen({ port: 3003, hostname: "0.0.0.0" });
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

One would expect that swagger would be exposed at /api/swagger. Which it is, but opening the page you get the following error:
slika

The JSON is resolvable at /api/swagger/json, under the /api prefix as expected, but the swagger try to fetch it from /swagger/json.

`scalarConfig.spec.url` is resolved incorrectly when the `path` option contains a subdirectory.

The changes introduced in #59 breaks in the case where swagger is mounted with path in the subdirectory.

const app = new Elysia()
  .use(
    swagger({
      path: "/services/docs",
    })
  )
  .get("/services/hello", () => ({ ok: true }));

When going to /services/docs, I get a blank page. This is because Scalar tries to load the data from /services/services/docs/json.

image

Current workaround is to manually override the scalarConfig

const app = new Elysia()
  .use(
    swagger({
      path: "/services/docs",
      scalarConfig: {
        spec: { url: "/services/docs/json" },
      },
    })
  )
  .get("/services/hello", () => ({ ok: true }));

Ordering of tags changes on every save

Hi team. Can we please enforce a ordering of the tags and endpoints?

I would be very happy if we could sort by: tag name, then, the endpoint URL (ascending)

Every time I made a change to endpoints, my Swagger documentation orders differently.

How to reproduce:

  1. Create 3-4 endpoints with different tags
  2. Change something in one of the endpoints
  3. Hit the save button and refresh documentation
  4. The order of the endpoint will change

Swagger doesn't work with Reference Models

When using reference models with swagger we hit a NOT_FOUND error (elysia side)
and Resolver error at paths./experiences.post.requestBody.content.application/json.schema.$ref Could not resolve reference: Could not resolve pointer: /components/schemas/experienceRequest does not exist in document (Swagger side)

image

models/elysia/experienceRequest.models.ts

import Elysia, { t } from "elysia";

export const experienceRequest = new Elysia()
  .model({
    'experienceRequest': t.Object({
      type: t.String(),
      title: t.String(),
      summary: t.String(),
    })
  })

plugins/experiences.ts

import { Domain, Experience } from 'models/database';
import { experienceRequest } from 'models/elysia'
import { Elysia } from 'elysia';
import { corsConf } from './corsConf';
import { userLogged } from './userLogged';
import { CannotSaveExperienceError, DomainDoesNotExistError } from 'errors';

export const experiences = new Elysia()
  .use(corsConf())
  .use(experienceRequest)
  .get('/experiences', async () => await Experience.find())
  .use(userLogged)
  .post(
    '/experiences',
    async ({ body, userId }) => {

      const domain = await Domain.findOne({ admin: userId });

      if (!domain) throw new DomainDoesNotExistError(userId)

      let experience = new Experience()
      experience.title = body.title
      experience.summary = body.summary
      experience.type = body.type

      const result = await experience.save();

      if (!result) throw new CannotSaveExperienceError(userId)

      domain.experiences.push(result.id)

      await domain.save()

      return result

    },
    {
      body: 'experienceRequest',
      detail: {
        summary: 'Add new experience'
      }
    }
  )

index.ts

import mongoose from 'mongoose';
import { Elysia } from 'elysia'
import { experiences, googleAuth, domain } from 'plugins';
import { validateEnvironment } from './services/validation';
import swagger from '@elysiajs/swagger';
import { errors } from 'errors';

validateEnvironment();

await mongoose.connect(Bun.env.MONGO_URL ?? '');

const app = new Elysia()
  .use(swagger())
  .error(errors)
  .onError(({ code, error }) => {
    console.error(error)
    return error.message;
  })
  .use(domain)
  .use(googleAuth)
  .use(experiences)
  .listen({
    hostname: Bun.env.HOSTNAME || "::",
    port: Bun.env.PORT || 3000,
    tls: Bun.env.TLS_PASSPHRASE ? {
      cert: Bun.file('./cert.pem'),
      key: Bun.file('./key.pem'),
      passphrase: Bun.env.TLS_PASSPHRASE
    } : undefined
  })

console.log(`Running at http://${app.server!.hostname}:${app.server!.port} CORS allowed: ${JSON.stringify(Bun.env.ALLOWED_DOMAINS)}`)

export type Portfolio = typeof app;

Local swagger-ui CSS and JS distributable

theme = `https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`,

<script src="https://unpkg.com/swagger-ui-dist@${version}/swagger-ui-bundle.js" crossorigin></script>

Because this library is downloading swagger-ui-bundle.js and swagger-ui.css it means it cannot be used without internet access, or behind a many firewalled enterprise networks, unfortunately.

It would be nice if you could remove this internet dependency and package the required swagger files locally within this library :)

"Failed to load API definition."

When trying to access the swagger url, it greets me with the following error:
image
And then when going to the direct JSON endpoint it shows me this in the browser:

Object.entries requires that input parameter not be null or undefined

Though, this only happens when being on the experimental version from elysia. I need that version though, because of the other routing bug in latest 👀
I don't know if I should report it here or in the other repository.

Example Code:

import { Elysia } from "elysia";
import { swagger } from "@elysiajs/swagger";

export const app = new Elysia()
    .use(swagger({
        path: "/api/swagger",
        swagger: {
            info: {
                title: "e.bio API",
                version: "2.0.0-beta"
            }
        }
    }));

app.get("/api", () => {
    res.send("Hello World!");
});

Also, I don't know why, but the specified title and version aren't showing either within Swagger on elysia@latest since a few weeks.

Swagger does not generate documentation if path is set to the home directory.

Swagger doesn't appear to generate documentation if the path is set to '/', an example here:

import { Elysia } from 'elysia';
import { swagger } from '@elysiajs/swagger'

const app = new Elysia()
  .use(swagger({
    path: '/',
    documentation : {
      tags : [
        { name: 'tag1', description: 'desc1'},
      ]
    }
  }))
  .get('/test', () => "Test", {
    detail : {
      tags: ['tag1']
    } 
  })
  .listen(3000)

The above only generates the background page for the swagger documentation, while if you adjust the path parameter to anything else or even remove it, it appears to work.

I can write a workaround to achieve the intended effect, but it does appear to be a bug.

New version error

Updated to version "@elysiajs/swagger": "^0.7.2", and am now getting the following error when I navigate to the swagger url:

🦊 Elysia is running at localhost:port
56 |                 registerSchemaPath({
57 |                     schema,
58 |                     hook: route.hooks,
59 |                     method: route.method,
60 |                     path: route.path,
61 |                     models: app.definitions.type,
                                ^
TypeError: undefined is not an object (evaluating 'app.definitions.type')
      at /node_modules/@elysiajs/swagger/dist/index.js:61:28
      at forEach (:1:20)
      at /node_modules/@elysiajs/swagger/dist/index.js:55:12
GET - /swagger/json failed

Reverting to version 0.6.2 resolves the error

Contact URLs are not correct

I have the following swagger options object:

const swaggerConfig = {
  path: "api-docs",
  documentation: {
    info: {
      title: "MyAPI",
      version: "1.0.0",
      description: "API Description",
      contact: {
        name: "My Name",
        email: "[email protected]",
      },
    },
  },
};

And the fields:

  • documentation -> info -> contact -> url
  • documentation -> externalDocs -> url

Show the url: localhost:8000/{url} instead of: url at the Swagger web interface.
I mean the URLs are relative to the actual URL and not absolute as they should be.
The URLs should be edited removing the actual URL where the Swagger web interface is at.

20230713_00h14m00s_grim

Instead of ...documentation.info as is I think the URL element has to be changed before, for example wrapping the actual object with a function that receive the object and the path (you already have it at this context) and then replaces all the path occurrences by an empty string. It's an idea feel free to change the approach ;-)

Thanks for the amazing framework and each of the plugins, they are awesome <3

No endpoints in definition when using `aot: false`

I would love to use this on cloudflare but when I configure elysia with aot: false it will not generate the endpoints in the swagger definition.

Is this a fundamental caveat of using dynamic mode because types and endpoints can not be inferred without this step, or is there a way to make it work with dynamic mode?

See also discussion here: elysiajs/elysia#58

Document setting content-type of response object

I am attempting to use a non default content type "text/event-stream" and have a very hard time figuring out how to specify this.

The content type is taken from app.routes -> hooks.type, but how do you actually set this value? Can an example please be added to the documentation?

Incorrect type for `t.File` and `t.Files`

When using t.File or t.Files, the type is not displayed correctly.

For swagger-ui:

  • It does not render the endpoint params properly and gives me this error: "😱 Could not render Parameters, see the console."
    2024-04-04-170852_830x845_scrot

For scalar:

  • Shows up as the literal "File"
    2024-04-04-170733_125x22_scrot

Import named 'DEFS' not found in module

With Elysia 0.2.6 this module fails looking for DEFS

Import named 'DEFS' not found in module

in index.ts line 1
import { type Elysia, SCHEMA, DEFS } from 'elysia'

Schema properties not being set in the proper place for schema validation

Hi! I have the following Elysia request:

.get('/:id', (c) => getEventDeploy(c.params.id, c.headers), {
    params: getDeployParamsSchema,
    detail: {
      summary: 'Returns the progress entries for the event with the given id.'
    },
    type: 'application/json'
  })

The Swagger generator generates an invalid entry for this request by default and the validation errors are as follows:

     "get": {
        "parameters": [
          {
            "pattern": "^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$",
            "default": "",
            "description": "The event id to get logs for",
            "type": "string",
            "in": "path",
            "name": "id",
            "required": true
          }
        ],
        "operationId": "getById",
        "summary": "Returns the progress entries for the event with the given id.",
        "responses": { "200": {} }
      }

image

In order to get a properly validated parameters object, I have to specify the following manually in parameters:

.get('/:id', (c) => getEventDeploy(c.params.id, c.headers), {
    params: getDeployParamsSchema,
    detail: {
      summary: 'Returns the progress entries for the event with the given id.',
      parameters: [
        {
          schema: getDeployParamsSchema.properties.id,
          name: 'id',
          in: 'path',
          required: true
        }
      ]
    },
    type: 'application/json'
  })

This generates the following schema, which does not error out at param validation:
image

Should this (generating the schema object for each param) not be the default behavior? Thanks!

Swagger not working with `@elysiajs/cors`

When ever I use elysia swagger with elysiajs/cors plugin swagger page doesn't loads and shows a blank page.
When i remove this line app.use(cors()); it starts to work.

version of each pakage are here.
"elysia": "^1.0.9",
"@elysiajs/cors": "^1.0.2"
"@elysiajs/swagger": "^1.0.3"

`basePath` Swagger Object configuration does not seem to be supported

Bun v1.0.15
Elysia: v0.8.8
Elysia/swagger: v0.8.0

When adding a prefix to my Elysia configuration:

export const elysia = new Elysia({ prefix: BASE_PATH })

I believe I would need to be able to then apply a basePath configuration per the Swagger object config: https://swagger.io/specification/v2/

// pseudo code that does not appear to function as expected
.use(swagger({
  documentation: {
    basePath: BASE_PATH,
    info: {}
  }
}))

The basePath does not appear to be a property of the defined type for the documentation configuration, but this is where it seems to logically appear in the swagger configuration.

Routes registered with app.all are not correctly expanded in the swagger file

When registering a route with the all method the generated swagger blindly copies the httpMethod which is not valid according to openapi 3.0.3 spec.

app
  .use(swagger())
  .all("test",() => {});
  "/test": {
    "all": {
      "operationId": "allTest",
        "responses": {
          "200": {}
        }
      }
  },

The only valid methods are get, put, post, delete, options, head, path, trace https://swagger.io/specification/

Here we probably should filter by valid methods and in case of all call the register function multiple times.

elysia-swagger/src/index.ts

Lines 132 to 143 in 8eb03f0

routes.forEach((route: InternalRoute) => {
registerSchemaPath({
schema,
hook: route.hooks,
method: route.method,
path: route.path,
// @ts-ignore
models: app.definitions.type,
contentType: route.hooks.type
})
})
}

Enums aren't generated to OAS/Swagger Specification

I might be doing something wrong, but when i make an endpoint with an enum in the body i don't get the enum in the swagger file

import { Elysia, t } from 'elysia'

export const plugin = new Elysia({
    prefix: '/a'
})
    .post('/hello-world', ({ body }) => body, {
        body: t.Object({
            type: t.Enum({
                MOD: 'mod_skin',
                VIP: 'vip_skin',
            })
        }),
    })

the output is

"requestBody": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "type": {
                  "anyOf": [
                    {
                      "const": "mod_skin",
                      "type": "string"
                    },
                    {
                      "const": "vip_skin",
                      "type": "string"
                    },

vs

schema:
            type: string
            enum: [asc, desc]

so the rendered output is either

Scalar
image
Swagger UI
image

let me know if it's something I can change or if it's something we need to add to the swagger generation code ✨

Swagger-specific options

I would like to have persistAuthorization set to true, near the "window.ui = SwaggerUIBundle({" code in src/index.ts

I guess it would be just great to have an argument called "swaggerParameters", a Record<string, string>, that would be put after "dom_id: '#swagger-ui',"

Empty page on refresh when using global cookie signature

When using global cookie signature, swagger docs works on first visit but returns empty page on following refreshes

import swagger from "@elysiajs/swagger";
import { Elysia, t } from "elysia";

const app = new Elysia({
  cookie: {
    secrets: "test",
    sign: ["test"],
  },
})
  .use(swagger())
  .get("/", () => "hi", {
    response: t.String({ description: "sample description" }),
  })
  .listen(3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

[Question] How simple grouping end point with tags?

How simple grouping end point with tags? for example this is my route code

import { Elysia } from "elysia";
import { Post } from '../controllers/post'

export const post = new Elysia({ prefix: '/post' })
    .get("/", () => Post.index(), { detail: { tags: ['Post'] } })
    .get("/:id", ({ params }) => Post.show(params.id), { detail: { tags: ['Post'] } })
    .post("/", () => Post.create(), { detail: { tags: ['Post'] } })
    .put("/:id", ({ params }) => Post.update(params.id), { detail: { tags: ['Post'] } })
    .delete("/:id", ({ params }) => Post.delete(params.id), { detail: { tags: ['Post'] } })

I must do repeat { detail: { tags: ['Post'] } in every route for grouping it.
Screenshot 2023-09-16 at 15 41 48

is there simple way to do that?

and then, could we add this params in file as comment, for example

// tags: Post
class Post {
  // description: 'get all posts'
  static async index () {
    return [...]
  }
}

incorrect work with type recursion

If we set the response type as an object containing recursion, an error occurs when opening the endpoint in swagger (and incorrect types are displayed in the example), while scalar stops working altogether.

Full code example:

import { Elysia, t } from "elysia";
import { swagger } from "@elysiajs/swagger";

const app = new Elysia()
  .use(
    swagger({
      provider: "swagger-ui", // scalar doesn't work bruh
      path: "/docs",
      documentation: {
        info: {
          title: "test",
          version: "1.0.0",
        },
        tags: [
          { name: "App", description: "General endpoints" },
          { name: "Users", description: "User endpoints" },
        ],
      },
    })
  )
  .get(
    "/test",
    () => {
      return {
        test: "321",
        part: {
          test: "32133",
        },
      };
    },
    {
      response: {
        200: t.Recursive((This) =>
          t.Object({
            test: t.String(),
            part: t.Optional(This),
          })
        ),
      },
    }
  )
  .listen({
    port: 3000,
    hostname: "0.0.0.0",
  });

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

изображение
изображение

hide endpoint

is is possible to hide specific endpoint from swagger doc ?

Swagger UI doesn't render t.Files() in body validation.

Hello there,
I hope you are well.

I have a RESTful API written in Bun using Elysia. And I integrated @elysia-swagger. In one of my endpoints where I upload multiple files of the same fieldname and validate the body like below.

app.post("/", ({body}) => files, { body: { files: t.Files() } })

When I select application/form-data, while trying to upload files from Swagger UI, the exception is thrown. The error message is 😱 Could not render Parameters, see the console.

Error in console is
SyntaxError: Unexpected token 'F', "Files" is not valid JSON

Thanks!

OpenAPI 3.1

Is it easy to support OpenAPI 3.1? I want to add null to my types without the Swagger parsers complaining.

Loads fonts from google

Hi there, I was experimenting with elysia for the first time with this simple example:

import { Elysia, t } from "elysia";
import { cors } from '@elysiajs/cors'
import { swagger } from '@elysiajs/swagger'

const app = new Elysia().use(cors()).use(swagger()).get('/id/:id', ({ params: { id } }) => id, {
        params: t.Object({
            id: t.Numeric()
        })
    }).get("/", () => "Hello Elysia").listen(3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

I visited http://localhost:3000/swagger to see what the interface looks like and I noticed a request to google is being sent to fetch some fonts.
Somewhat annoyed to see this (after all, it's just a local test of the software and I find myself tracked indiscriminately), I went to search in the repo for references regarding the request without finding anything.
So is it a Swagger issue and should I report it to them or is it due to the implementation done in Elysia?

Unable to use local $ref components

I had a functional work-around for version 0.8.0, but 0.8.1 replaced the SwaggerUI with Scalar, which does not accept this work-around. I have tried many things, but been unable to find a working solution for referencing schema components that exist within the same OpenAPI file.

I specify the schemas with new Elysia().model({ User: UserModel })

This puts the UserModel into #/components/schemas/User in the OpenAPI file.

I would like to reference this model (or several) in a response or body, by wrapping it in an object, like so:

new Elysia()
  .model({ User: UserModel })
  .get("/users", () => {
    return { users: [] }
  }, {
    response: t.Object({
      users: t.Array(
        // Some reference to '#/components/schemas/User'
      )
    })
  })

Currently, the only way I've found to somewhat get this effect is to put the UserModel into the array, but this breaks the link to the existing schema. The end result is that the OpenAPI file duplicates the schema, inflating it exponentionally depending on how many times you wish to use it.

I want the file to appear like this

{
  "openapi": "3.1.0",
  "servers": [
    {
      "url": "http://localhost:3000",
      "description": "Test server"
    }
  ],
  "components": {
    "schemas": {
      "User": {
        "description": "User",
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "email": {
            "format": "email",
            "type": "string"
          },
          "id": {
            "readOnly": true,
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "email",
          "id",
          "name"
        ]
      }
    }
  },
  "info": {
    "title": "Custom API",
    "description": "These are the custom docs",
    "version": "1.0.0"
  },
  "paths": {
    "/users/": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "users": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    }
                  },
                  "required": [
                    "users"
                  ]
                }
              }
            }
          }
        },
        "operationId": "getUsers",
        "summary": "Get all users"
      }
    }
  }
}

Instead it appears like this

{
  "openapi": "3.1.0",
  "servers": [
    {
      "url": "http://localhost:3000",
      "description": "Test server"
    }
  ],
  "components": {
    "schemas": {
      "User": {
        "description": "User",
        "additionalProperties": false,
        "type": "object",
        "properties": {
          "email": {
            "format": "email",
            "type": "string"
          },
          "id": {
            "readOnly": true,
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "email",
          "id",
          "name"
        ]
      }
    }
  },
  "info": {
    "title": "Custom API",
    "description": "These are the custom docs",
    "version": "1.0.0"
  },
  "paths": {
    "/users/": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "users": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "name": {
                            "type": "string"
                          },
                          "email": {
                            "format": "email",
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "email"
                        ]
                      }
                    }
                  },
                  "required": [
                    "users"
                  ]
                }
              }
            }
          }
        },
        "operationId": "getUsers"
      }
    }
  }
}

Some things I have tried

With SwaggerUI, I was able to get this to work with t.Array(t.Ref(UserModel)), however Scalar does not like this approach, instead attempting to fetch GET /User as it seems to think the schema is external to the file. I'm not sure I can fault Scalar for this though, as it seems the format was incorrect originally despite working in SwaggerUI.

To get it to work with SwaggerUI, the UserModel was required to specify the $id field. It seems by setting it to "User", the file ended up with "$ref": "User", and the local schema included the "$id": "User" field. I think the issue is that the $ref should be #/components/schemas/User, not just User. There may be other issues aside from this, but this is the most prominent issue I can spot. I've been reading the OpenAPI v3.1 spec, and I don't think it allows referring to local schemas simply by name, despite them having an $id, but I could be wrong. Either way, Scalar breaks with how it currently is.

Using:

  • elysia 0.8.9
  • elysia-swagger 0.8.3
  • bun 1.0.20

Reproduction

Open to see

const UserModel = t.Object(
  {
    id: t.Integer({ readOnly: true }),
    name: t.String(),
    email: t.String({ format: "email" }),
  },
  {
    $id: "User",
  }
);

const app = new Elysia()
  .use(swagger({
    documentation: {
      openapi: "3.1.0"
    }
  }))
  .model({ User: UserModel })
  .get("/users/", () => ({ users: [] }), {
    response: t.Object({
      users: t.Array(t.Ref(UserModel)),
    }),
    type: "application/json"
  });

Go to /swagger/json to get the resulting file (Scalar at /swagger just generates a blank page)
The file looks like this:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Elysia Documentation",
    "description": "Development documentation",
    "version": "0.0.0"
  },
  "paths": {
    "/users/": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "users": { "type": "array", "items": { "$ref": "User" } }
                  },
                  "required": ["users"]
                }
              }
            }
          }
        },
        "operationId": "getUsers"
      }
    }
  },
  "components": {
    "schemas": {
      "User": {
        "$id": "User",
        "type": "object",
        "properties": {
          "id": { "readOnly": true, "type": "integer" },
          "name": { "type": "string" },
          "email": { "format": "email", "type": "string" }
        },
        "required": ["id", "name", "email"]
      }
    }
  }
}

If you put it into https://editor-next.swagger.io/ it renders more or less correctly

If you put it into https://docs.scalar.com/swagger-editor you can see that it fails to locate the schema, falling back to null.
Modify the $ref to be #/components/schemas/User and it will work.

Array type does not work

When an array is passed in the response it ends up not recognizing it

response: t.Array(userDTO)

Screenshot 2023-09-28 at 10 11 55

Now if an object is passed to be surrounded by an array, it works perfectly.

response: userDTO

Screenshot 2023-09-28 at 10 12 07

Merge guard and route's response schema

I added a response with a status code of 400 to the guard hook and a response of 200 to the route. I want to automatically merge rather than overwrite guard hooks in swagger

import { Elysia, t } from "elysia";
import jwt from "../middleware/1.jwt";
import { find } from "lodash-es";

const users = [
  {
    name: "xxx",
    passwd: "123",
  },
];

export default new Elysia({ prefix: "/user" }).use(jwt).guard(
  {
    detail: {
      tags: ["User"],
    },
    error: ({ error }) => {
      return {
        message: error.message,
      };
    },
    response: {
      "400": t.Object({
        message: t.String(),
      }),
    },
  },
  (app) =>
    app.post(
      "/login",
      async ({ jwt, body: { name, passwd } }) => {
        const user = find(users, { name, passwd });
        if (!user) {
          throw new Error("mm");
        }
        return {
          token: await jwt.sign({
            name: user.name,
          }),
        };
      },
      {
        body: t.Object({
          name: t.String(),
          passwd: t.String(),
        }),
        response: {
          200: t.Object({ token: t.String() }),
        },
      }
    )
);

image

Most OpenAPI gen. tools require `operationId` to be set for function properly

Hello! THANKS for making this, I love Elysia API, best I've seen for a web server so far!


3 generators tested, and none are working becauseoperationId😔 is mandatory for them.

Here,

const schema = value[method]

adding a:

method.operationId = "…"

With two possible strategies I can think of:

  1. Auto-generating name from path

DELETE /document/{id} > deleteDocumentById

cool for simple cases, but complicated with /document/{id}/stuff/foo/{bar} > deleteDocumentByIdStuffFooByBar lol

  1. Using decorators

.decorateApi("operationId", "foobar")

.decorateApi("description", "my cool op.")

Wha d'ya think?

Cheers.

Issue: Lockdown

Not sure how to report this issue because I have no idea what is this issue about.

When you open swagger endpoint and open the console, the issue is there.

Captura de Pantalla 2023-09-21 a las 1 53 46

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.