ncc icon indicating copy to clipboard operation
ncc copied to clipboard

cant use with swagger

Open yeli19950109 opened this issue 5 years ago • 11 comments

cant use with swagger for nestjs

yeli19950109 avatar May 23 '19 11:05 yeli19950109

Hello @yeli19950109

Can you create an example that fails with ncc build?

styfle avatar May 24 '19 17:05 styfle

I'm experiencing the same issue, the path to the Swagger UI assets is no longer correct after building the app with ncc.

After some playing around, however, I've seem to found a workaround: Serving the static folder manually, after the Swagger setup. Here's a code snippet, I'm using /swagger as the public URL:

// ...

SwaggerModule.setup('/swagger', app, document);

app.useStaticAssets(join(__dirname, '/static'), {
  prefix: '/swagger',
});

// ...

Plus, I had to install fastify-swagger although I'm just using express in NestJS ... just wtf?

This all works for now, but surely is not a very pretty solution ...

dominique-mueller avatar Jul 23 '19 17:07 dominique-mueller

The real issue is probably how swagger-ui itself gets built and served, and not necessarily how ncc tries to bundle it up. Related issue: https://github.com/scottie1984/swagger-ui-express/issues/114.

dominique-mueller avatar Jul 23 '19 17:07 dominique-mueller

Any updates on this one ??

p.s: @dominique-mueller I have tried your proposed solution and it seems that I might be missing something out. Could you be kind enough to help me out with this issue ??

mohammadzainabbas avatar Mar 30 '20 10:03 mohammadzainabbas

@mohammadzainabbas Sure, I took the time to prepare a repository here: https://github.com/dominique-mueller/ncc-nestjs-swagger-experiment. It's a working example, so you can clone it and play around with it. I also wrote a quick explanation in the README about what has to be done to get Swagger working in an existing ncc-NestJS project. Hope it helps!

dominique-mueller avatar Apr 01 '20 17:04 dominique-mueller

I have issues with swagger-ui-express as well after using ncc. @dominique-mueller, thank you for your example. I am wondering if it would be possible to achieve similar fix with express only? I am not using nestjs in my project.

eMarek avatar Aug 06 '20 12:08 eMarek

So my understanding of the issue is that swagger-ui works like this:
When a browser requests the swagger documentation URL (e.g. https://example.com/swagger/):

  • the swagger-ui module builds a dynamic html file and returns it to the client
    • this contains relative links to some files: e.g.swagger-ui-bundle.js, swagger-ui.css etc.
    • so these files must be accessible from the client browser

"normal" build (i.e. without ncc)

installed packages

  • in the root of our project we have all dependencies in node_modules available
  • according to the NestJs Swagger Docs we install the npm packages:
    • @nestjs/swagger
    • swagger-ui-express

dependencies

  • We use the NestJs SwaggerModule in our code, which will require('swagger-ui-express'), which in turn has a dependency on:
  • swagger-ui-dist which is a small package that contains the assets that swagger-ui needs at runtime: e.g.swagger-ui-bundle.js, swagger-ui.css etc.

Runtime

When the server is running and the browser visits the swagger URL (e.g. https://example.com/swagger/):

  • express will handle the request
  • the GET request for this path has been registered by the NestJs SwaggerModule
    • and this returns the dynamic HTML page created by swagger-ui-express
    • this HTML contains relative links: so the browser will send new requests to get these files e.g. https://example.com/swagger/swagger-ui-bundle.js
      • these requests are also handled by express and the routes have been registered by swagger-ui-express , which just forwards the GET requests to the swagger-ui-dist package
      • this works, because the package is required and NodeJs will resolve it according to its algorithm which in this case will find the swagger-ui-dist package in the top-level node_modules folder

"ncc" build:

installed packages

None: in this case we don't have access to node_modules/swagger-ui-dist
i.e. the reason to use ncc is that we don't need the large node_modules folder.

Runtime

So when we don't do anything special the swagger-html page will not work, because the requests for the relative links in the dynamic HTML cannot be resolved (express will return a HTTP 404 - Not Found error)

Fix

To fix this we must somehow provide these files. This requires 2 steps

  1. copy the relevant files to a folder which will be accessible (by node) at runtime, when we start the ncc output
  2. tell express to send those files to the client-browser when the correct URL is requested

copy files

This depends on your build tools: e.g. we use angular-cli, thus we can simply use assets to copy the relevant files, e.g.

"assets": [
   {
   "glob": "**/*.{js,css,html,png}",
   "input": "./node_modules/swagger-ui-dist/",
   "output": "./dist/assets/swagger-ui-dist/"
   },
   ....
]

Where dist is the ncc output dir (i.e. where the index.js or main.js file will be created)

serve files

When we use NestJs we can use this:

app.useStaticAssets(path.join(__dirname, 'assets/swagger-ui-dist/'), {
  prefix: '/swagger'
});

where app is our NestExpressApplication.
So basically this just configures express to return the files in the assets/swagger-ui-dist/ folder when the URL starts with /swagger: e.g. https://example.com/swagger/swagger-ui-bundle.js

Why installing "fastify-swagger" seems to work

This refers to the explanation in the ncc-nestjs-swagger-setup test project

I guess the reason is this:

When ncc analyses our code, it finds require('fastify-swagger') in the SwaggerModule.
When the package is installed (i.e. exists in node_modules), ncc will process it.
When the webpack-asset-relocator-loader (used by ncc) will notice that the file prepare-swagger-ui.js needs the files in the static folder:
image

And thus it will copy these files to the static folder in our dist dir.
Now we could serve these files and the swagger URL may work.

I think this may not work reliably, because there is no guarantee that the swagger-ui-dist version which is copied from the fastify-swagger package is compatible to the version used by swagger-ui.

So I think it's better to

  • not include fastify-swagger
  • and instead copy the files from /node_modules/swagger-ui-dist

tmtron avatar Mar 11 '21 15:03 tmtron

I was experiencing the same issue. Fortunately I got it to work without ncc. My solution is a bit hacky. Check out my solution on stack overflow: https://stackoverflow.com/a/74708365/13701992

onihani avatar Dec 06 '22 20:12 onihani

This is how I solved the problem. Deploying Platform: Vercel

image

ramazansancar avatar Dec 12 '22 07:12 ramazansancar

I wrote a patch to solve this problem. use https://unpkg.com/[email protected]/ as publicUrl

diff --git a/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js b/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js
index 6f47648..8776139 100644
--- a/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js
+++ b/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js
@@ -1,8 +1,8 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.jsTemplateString = exports.htmlTemplateString = exports.favIconHtml = void 0;
-exports.favIconHtml = '<link rel="icon" type="image/png" href="<% baseUrl %>favicon-32x32.png" sizes="32x32" />' +
-    '<link rel="icon" type="image/png" href="<% baseUrl %>favicon-16x16.png" sizes="16x16" />';
+exports.favIconHtml = '<link rel="icon" type="image/png" href="<% publicUrl %>favicon-32x32.png" sizes="32x32" />' +
+    '<link rel="icon" type="image/png" href="<% publicUrl %>favicon-16x16.png" sizes="16x16" />';
 exports.htmlTemplateString = `
 <!-- HTML for static distribution bundle build -->
 <!DOCTYPE html>
@@ -10,7 +10,7 @@ exports.htmlTemplateString = `
 <head>
   <meta charset="UTF-8">
   <title><% title %></title>
-  <link rel="stylesheet" type="text/css" href="<% baseUrl %>swagger-ui.css" >
+  <link rel="stylesheet" type="text/css" href="<% publicUrl %>swagger-ui.css" >
   <% favIconString %>
   <style>
     html
@@ -71,8 +71,8 @@ exports.htmlTemplateString = `
 
 <div id="swagger-ui"></div>
 
-<script src="<% baseUrl %>swagger-ui-bundle.js"> </script>
-<script src="<% baseUrl %>swagger-ui-standalone-preset.js"> </script>
+<script src="<% publicUrl %>swagger-ui-bundle.js"> </script>
+<script src="<% publicUrl %>swagger-ui-standalone-preset.js"> </script>
 <script src="<% baseUrl %>swagger-ui-init.js"> </script>
 <% customJs %>
 <% customJsStr %>
diff --git a/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js b/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js
index c067725..d60f616 100644
--- a/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js
+++ b/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js
@@ -55,6 +55,7 @@ function buildSwaggerHTML(baseUrl, swaggerDoc, customOptions = {}) {
         .replace('<% explorerCss %>', explorerCss)
         .replace('<% favIconString %>', favIconString)
         .replace(/<% baseUrl %>/g, baseUrl)
+        .replace(/<% publicUrl %>/g, 'https://unpkg.com/[email protected]/')
         .replace('<% customJs %>', toTags(customJs, toExternalScriptTag))
         .replace('<% customJsStr %>', toTags(customJsStr, toInlineScriptTag))
         .replace('<% customCssUrl %>', toTags(customCssUrl, toExternalStylesheetTag))

Patch file

@nestjs+swagger+7.1.10.patch

kangfenmao avatar Sep 09 '23 03:09 kangfenmao

My swagger UI now shows with the correct CSS/JS after relocating it. But it still shows up as with the example "Petstore". Is there some kind of document (json) that's missing?

gaffarmalik avatar Sep 10 '24 11:09 gaffarmalik