Search code examples
swaggerswagger-uiserverlessvercel

Swagger UI & Vercel: Unexpected token < in JSON at position 1


Code

Full codebase & folder structure can be seen in GitHub

Here is the Swagger related route (had to make it a standalone server)

// api/v1.ts

import express = require("express");
import swaggerJSDoc = require("swagger-jsdoc");
import swaggerUi = require("swagger-ui-express");
import packageJSON = require("../package.json");
import path = require("path");

const app = express();
app.use(express.json());
app.use(express.static(path.resolve(__dirname, "../", "public")));

const swaggerSpec = swaggerJSDoc({
  swaggerDefinition: some_spec,
  apis: ["api/*"]
});

const cssOpts = some_css_override;

app.use("/api/v1", swaggerUi.serve, swaggerUi.setup(swaggerSpec, cssOpts));

module.exports = app;

Problem

When I run vercel dev (locally- localhost:3000/api/v1), I see documentation as expected: Local (vercel dev)

However when I push my code to a branch which triggers a vercel build, I see the following: Vercel Build

Checking the console, I see:

DevTools failed to load source map: Could not parse content for https://colormaster-1unjfn63b-lbragile.vercel.app/api/v1/swagger-ui-bundle.js.map: Unexpected token < in JSON at position 1

DevTools failed to load source map: Could not parse content for https://colormaster-1unjfn63b-lbragile.vercel.app/api/v1/swagger-ui-standalone-preset.js.map: Unexpected token < in JSON at position 1

Even though they respond with 200 Network Responses

I understand that this has something to do with JSON.parse() of HTML content, but not sure how to fix this. Any ideas?


Solution

  • I am facing the exact same problem of you, trying without success to deploy Swagger to Vercel with Express.

    I did one step more, and now I'm seen an error in my console:

    Refused to apply style from 'https://myurlishere.vercel.app/api-docs/swagger-ui.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

    What I did was, adding a file routes.ts

    import { Router } from 'express';
    import LanguageController from './controller/LanguageController';
    import WordController from './controller/WordController';
    
    const routes = Router();
    
    routes.get("/word", WordController.find);
    routes.get("/word/:wordName/language/:languageId", WordController.findByWordAndLanguage);
    routes.post("/word", WordController.create);
    routes.get("/language", LanguageController.find);
    
    export default routes;
    

    And my server.ts looks like that:

    import mongoose from 'mongoose';
    import routes from './routes';
    
    const express = require("express");
    
    if (process.env.NODE_ENV !== 'production') {
        require('dotenv').config();
    }
    
    const app = express();
    const cors = require('cors');
    
    
    mongoose.connect(process.env.MONGODB_URI || "", {
        dbName: "WordsThatIKnowMongoDB"
    })
        .then(() => console.debug("Database connected!"))
        .catch(err => { console.debug(err) });
    
    app.use(express.json());
    app.use(express.static("/api-docs"));
    
    app.use(cors());
    app.use(routes);
    
    const swaggerUi = require('swagger-ui-express');
    const swaggerDocument = require('./swagger.json');
    
    routes.use('/api-docs', swaggerUi.serve);
    routes.get('/api-docs', swaggerUi.setup(swaggerDocument));
    
    
    app.listen(5000, () => {
        console.debug("Running on port 5000.");
    });
    
    // Export the Express API
    module.exports = app;
    

    You will see in the file above that I changed app. to routes. like this:

    routes.use('/api-docs', swaggerUi.serve);
    routes.get('/api-docs', swaggerUi.setup(swaggerDocument));
    

    I still can't solve this problem, but maybe this new error can help you find the solution. I'm also looking for that.

    • EDIT: It's solved.

    This is the code that solved my problem:

    server.ts

    import path from 'path';
    import cors from 'cors';
    import bodyParser from 'body-parser';
    import mongoose from 'mongoose';
    import routes from './routes';
    
    const express = require("express");
    const app = express();
    
    const ROOT_FOLDER = path.join(__dirname, '..');
    const SRC_FOLDER = path.join(ROOT_FOLDER, 'src');
    
    // parse requests of content-type - application/x-www-form-urlencoded
    app.use(bodyParser.urlencoded({ extended: true }));
    // parse requests of content-type - application/json
    app.use(bodyParser.json());
    
    app.use(cors());
    app.use(routes);
    
    if (process.env.NODE_ENV !== 'production') {
        require('dotenv').config();
    }
    
    mongoose.connect(process.env.MONGODB_URI || "", {
        dbName: "WordsThatIKnowMongoDB"
    })
        .then(() => console.debug("Database connected!"))
        .catch(err => { console.debug(err) });
    
    const swaggerUi = require('swagger-ui-express');
    const swaggerDocument = require('./swagger.json');
    const options = { customCssUrl: '/public/swagger-ui.css', customSiteTitle: "The Words That I Know API - Swagger" };
    
    app.use('/public', express.static(path.join(SRC_FOLDER, 'public')));
    app.use('/', swaggerUi.serve);
    app.get('/', swaggerUi.setup(swaggerDocument, options));
    
    app.listen(5000, () => {
        console.debug("Running on port 5000.");
    });
    
    export default app;
    

    Don't forget to put the styles from Swagger at '/public/swagger-ui.css'. Create a public folder inside src and include a swagger-ui.css file. Inside of this, past swagger styles. You can find swagger styles using inspect on browser, and going to source tab. There you'll find the swagger-ui.css file; remove the commented line after pasting the styles code. If you prefer an easy way to get the styles code, get this file. https://github.com/deywersonp/ghibli-50-api/blob/main/src/public/css/swagger-ui.css