Installing Swagger RESTful API documentation tool on an nginx server

Posted on Friday, June 7, 2013


Swagger is a great program for documenting your RESTful APIs.  It is made by the good folks at Reverb.  It is open source, apache license and can be found here https://developers.helloreverb.com/swagger/ [1]

Their demo page is located at http://petstore.swagger.wordnik.com/ [2]
And the git repository for this software is located at https://github.com/wordnik/swagger-core [3]

In this document I am going over setting up an nginx server, installing swagger and setting up a swagger API documents in simple static json files.  Also I want to do a simple password protection of my swagger API notes as I am currently doing this just for internal use.  (I just have never done this in nginx so I want to document it)

This was installed on Ubuntu 12.10




Install NGINX


> sudo apt-get update
> sudo apt-get upgrade
> sudo apt-get install nginx




I prefer creating my own location to place my static web pages, so I will create a default folder for my website and update the nginx.conf file to use it.


> sudo mkdir /swagger


Update the /etc/nginx/nginx.conf file to use the new directory


> sudo vi /etc/nginx/nginx.conf


Edit the document to the following  (my server happens to be running on ip address 192.168.0.9)


user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

error_log /var/log/nginx/error.log;

events {
  worker_connections 1024;
  use epoll;
  multi_accept on;
}

http {

  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  server {
    listen       80;
    keepalive_timeout    70;

    server_name  192.168.0.9;
    # doc root
    root /swagger;
  }
}



Restart the nginx server


> sudo /etc/init.d/nginx restart








Download swagger


You can get the swagger code by using the git tool.  Ubuntu 12.10 does not have git installed by default.  Run this command to install git.


> sudo apt-get install git


Use git to obtain the swagger repository


> git clone https://github.com/wordnik/swagger-ui.git



You need to copy the contents of the dist folder located in the swagger-ui folder to the default web site folder of the nginx server.  Here is the command to copy those files over.


> sudo cp -R swagger-ui/dist/* /swagger




Open your web browser and point it to your nginx server.   Mines happens to be on 192.168.0.9 so I opened http://192.168.0.9/




Success!


You can click on this example and see how it runs just fine.








Password Protect folder


I used some information from this site http://www.howtoforge.com/basic-http-authentication-with-nginx [4] to get some information on setting up .htpasswd with nginx.  The nginx noges on auth_basic can be found here http://wiki.nginx.org/HttpAuthBasicModule [5].  Another site I found useful was http://www.cowboycoded.com/2011/01/12/password-protect-your-entire-site-with-nginx/ [6]


To create a password file you need the htpasswd tool which can be obtained via the apache2-utils.  Use this command to install the apache2-utils


> sudo apt-get install apache2-utils



Create a .htpasswd file with your first user in it (with their passwd).  This command will create the .htpasswd with the user "patman" adjust it to your needs.


> sudo htpasswd -cm /swagger/.htpasswd patman


When you run this command it will ask you to type in your password.


For each additional user drop the -c (which creates a new file) .  Here is an example that will add a user to your .htpasswd file without creating a new file.


> sudo htpasswd -m /swagger/.htpasswd patman2






Update nginx to handle .htpasswd


> sudo vi /etc/nginx/nginx.conf


Edit the document to the following


user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

error_log /var/log/nginx/error.log;

events {
  worker_connections 1024;
  use epoll;
  multi_accept on;
}

http {
  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  server {
    auth_basic "Restricted";
    auth_basic_user_file /swagger/.htpasswd;

    listen       80;
    keepalive_timeout    70;

    server_name  192.168.0.9;
    # doc root
    root /swagger;
  }
}



This adds password protection to the entire site.


Restart the nginx server


> sudo /etc/init.d/nginx restart






Open your web browser and point it to your nginx server.   Mine happens to be at http://192.168.0.9/




Success!  It asked for a password.  And when I put my username password in it opened up just fine.





Write your own swagger json files


Now I am going to update and examine the default example files given to me by swagger.   My goal is to hand make some static swagger files that work.

Update /swagger/index.html


> cd /swagger
> sudo vi index.html




Here is the default index.html from the swagger example


<!DOCTYPE html>
<html>
<head>
    <title>Swagger UI</title>
    <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
    <link href='css/hightlight.default.css' media='screen' rel='stylesheet' type='text/css'/>
    <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
    <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
    <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
    <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
    <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
    <script src='lib/handlebars-1.0.rc.1.js' type='text/javascript'></script>
    <script src='lib/underscore-min.js' type='text/javascript'></script>
    <script src='lib/backbone-min.js' type='text/javascript'></script>
    <script src='lib/swagger.js' type='text/javascript'></script>
    <script src='swagger-ui.js' type='text/javascript'></script>
    <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>

    <script type="text/javascript">
        $(function () {
            window.swaggerUi = new SwaggerUi({
                discoveryUrl:"http://petstore.swagger.wordnik.com/api/api-docs.json",
                apiKey:"special-key",
                dom_id:"swagger-ui-container",
                supportHeaderParams: false,
                supportedSubmitMethods: ['get', 'post', 'put'],
                onComplete: function(swaggerApi, swaggerUi){
                        if(console) {
                        console.log("Loaded SwaggerUI")
                        console.log(swaggerApi);
                        console.log(swaggerUi);
                    }
                  $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
                },
                onFailure: function(data) {
                        if(console) {
                        console.log("Unable to Load SwaggerUI");
                        console.log(data);
                    }
                },
                docExpansion: "none"
            });

            window.swaggerUi.load();
        });

    </script>
</head>

<body>
<div id='header'>
    <div class="swagger-ui-wrap">
        <a id="logo" href="http://swagger.wordnik.com">swagger</a>

        <form id='api_selector'>
            <div class='input icon-btn'>
                <img id="show-pet-store-icon" src="images/pet_store_api.png" title="Show Swagger Petstore Example Apis">
            </div>
            <div class='input icon-btn'>
                <img id="show-wordnik-dev-icon" src="images/wordnik_api.png" title="Show Wordnik Developer Apis">
            </div>
            <div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl"
                                      type="text"/></div>
            <div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
            <div class='input'><a id="explore" href="#">Explore</a></div>
        </form>
    </div>
</div>

<div id="message-bar" class="swagger-ui-wrap">
    &nbsp;
</div>

<div id="swagger-ui-container" class="swagger-ui-wrap">

</div>

</body>

</html>



I am going to make a bare bones index.html  here is my simpler version


<!DOCTYPE html>
<html>
<head>
    <title>Swagger UI</title>
    <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
    <link href='css/hightlight.default.css' media='screen' rel='stylesheet' type='text/css'/>
    <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
    <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
    <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
    <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
    <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
    <script src='lib/handlebars-1.0.rc.1.js' type='text/javascript'></script>
    <script src='lib/underscore-min.js' type='text/javascript'></script>
    <script src='lib/backbone-min.js' type='text/javascript'></script>
    <script src='lib/swagger.js' type='text/javascript'></script>
    <script src='swagger-ui.js' type='text/javascript'></script>
    <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>

    <script type="text/javascript">
        $(function () {
            window.swaggerUi = new SwaggerUi({
                discoveryUrl:"http://petstore.swagger.wordnik.com/api/api-docs.json",
                dom_id:"swagger-ui-container",
                supportedSubmitMethods: ['get', 'post', 'put'],
            });

            window.swaggerUi.load();
        });

    </script>
</head>

<body>

<br/>
<div id="swagger-ui-container" class="swagger-ui-wrap">

</body>

</html>



If you open the site now you should see this




The header is now gone that allows you to load other swagger apis (if you submit the correct web address of your swagger json files to it)
And the /store api is now missing.


It is only missing because I removed this line from the javascript that created the SwaggerUI object



If you put it back in and reopen the site you will see the /store again




For now I am removing the apiKey because I want to start on the very basic level and continue building from there.


Update the index.html again.
Change the  discoveryURL to a local json file

Change


To




To reference a file at  http://192.168.0.9/api/api-docs.json



Create the api-docs.json file



> sudo mkdir /swagger/api
> cd /swagger/api
> sudo vi api-docs.json



Here is swaggers page on their resource listings  https://github.com/wordnik/swagger-core/wiki/Resource-Listing [7]




Here is the json file I came up with that only defines one api that is a simple get resource.


{
    "basePath": "http://192.168.0.9/",
    "resourcePath": "/",
    "swaggerVersion": "1.1",
    "apiVersion": "0.1",
    "apis": [
        {
            "path": "/jsonfiles/test",
            "format": "json",
            "description": "Download a json file",
            "operations": [
                {
                    "httpMethod": "GET",
                    "summary": "Downloads a json file",
                    "responseClass": "void",
                    "nickname": "jsonTest"
                }
            ]
        }
    ]
}




Here is a description of what is going on line by line  (as best as I can explain/understand it at this stage of learning swagger)


    "basePath": "http://192.168.0.9/",
    "resourcePath": "/",
    "swaggerVersion": "1.1",
    "apiVersion": "0.1",


basePath        
            The url location of your web based api.  (in my test case its 192.168.0.9  in a real case it would be your web site url to the base of your API)

resourcePath
            The relative path to the API from the basePath


swaggerVersion
            The swagger version you are using  (currently 1.0 and 1.1 are valid)

apiVersion
            The version number of the api being displayed  (your api version number… I guess it does not have to be a number)          




    "apis": [
        {
            "path": "/jsonfiles/test",
            "format": "json",
            "description": "Download a json file",



Here is the meat where you define your apis.  Swaggers api declaration page can be found here https://github.com/wordnik/swagger-core/wiki/API-Declaration [8]


apis
            A list of APIS (urls) that you want listed

path
            The relative path (from the resourcePath) to the resource you are defining


description
            The description of the resource (I do not see where this is used on the display page)





            "operations": [
                {
                    "httpMethod": "GET",
                    "summary": "Downloads a json file",
                    "responseClass": "void",
                    "nickname": "jsonTest"
                }
            ]



operations
            A list of operations allowed on this resource (GET, POST, PUT, and DELETE)

httpMethod
            This can be set to GET, POST, PUT, and DELETE

summary
            This description is shown next to the resource on the web page

responseClass
            This is what is returned from the method it can be void, primitive, complex or container.  For this simple case I made it void

nickname
            this is used by the code generator






Test it out





Reload the web page and you should see a simple slash





Click on the / and you will see all the resources that you defined (in this case just the 1)


Now click on the "Try it Out!" button.  This button will issue a GET to the defined resource.  (this is the really cool thing about swagger aside from being a great documentation tool it's also a good place to test



But the results in this case is a 404 error with a generated error page from the nginx server.   This is because I did not put anything at http://192.168.0.9/jsonfiles/test

So let me remedy that right now


> sudo mkdir /swagger/jsonfiles
> sudo vi /swagger/jsonfiles/test


I put the following simple json file into it


{
    "names": [
        {
            "firstName": "John",
            "lastName": "Smith"
        },
        {
            "firstName": "Fred",
            "lastName": "black"
        },
        {
            "firstName": "jeff",
            "lastName": "fischer"
        }
    ]
}


Reload the page again




Now it GETs the json file defined in the api


It also shows the response code and the response headers!
This could really replace using curl in a lot of simple quick tests that need to be repeated a few times.


The Content-Type for this json file is being served up as an application/octet-stream from my nginx server.   I would like to update the swagger api declaration to GET it as a application/json Content-Type.  I found this post talking about adding a feature like this in version 1.2 of swagger https://github.com/wordnik/swagger-ui/issues/119 [9]


OK, there is a way to do this, it took me some time but I figured it out.
Give me some grace as I may be long winded in explaining this.


First you need to update you api-docs.json file and change the responseClass in the operation to "String".  After you do this you need to add "produces" and specify the content types this resource can be obtained as.

Here is my updated api-docs.json


{
    "basePath": "http://192.168.0.9/",
    "resourcePath": "/",
    "swaggerVersion": "1.2",
    "apiVersion": "0.1",
    "apis": [
        {
            "path": "/jsonfiles/test",
            "format": "json",
            "description": "TEST Download a json file",
            "operations": [
                {
                    "httpMethod": "GET",
                    "summary": "Downloads a json file",
                    "responseClass": "String",
                    "nickname": "jsonTest",
                    "produces": [
                       "application/json",
                       "text/html"
                    ]
                }
            ]
        }
    ]
}


I set this to accept application.json and text/html

After this is updated reload your page




You should see that you now have a pull down showing which Response Content Types you can get.  Select one and click Try it Out!




It does load, but it gets the wrong Content-Type.  

I used fiddler http://fiddler2.com/ [9] to examine the web traffic back and forth.



I could see that when I tried to get application/json it did try to get that.

I want to make this a bit better test, while still being a static test.   I am going to update nginx to serve all files from the /jsonfile folder as application/json




Updating nginx to serve application/json


Open up the nginx conf file


> sudo vi /etc/nginx/nginx.conf


Edit the document to the following  (my server happens to be running on ip address 192.168.0.9)


user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

error_log /var/log/nginx/error.log;

events {
  worker_connections 1024;
  use epoll;
  multi_accept on;
}

http {

  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  server {
    auth_basic "Restricted";
    auth_basic_user_file /swagger/.htpasswd;

    listen       80;
    keepalive_timeout    70;

    server_name  192.168.0.9;
    # doc root
    root /swagger;

    location /jsonfiles {
      types  {}
      default_type application/json;
    }
  }
}


I just added



    location /jsonfiles {
      types  {}
      default_type application/json;
    }


Which says the default type for all files in this folder will be set to application/json


Restart the nginx server


> sudo /etc/init.d/nginx restart




WARNING:   when I reloaded the page again it cached the old page so it looked like it was not working.  I had to clear the cache to see the results I expected..

Now when I try it again I get the application/json I was expecting

If I change and try to get text/html  it seems to just be using the cache and I get the same results…   When I started a new browser and emptied the cache I got the same result when asking for text/html which makes sense as I set up the folder to do just that in nginx.


I think I will call this a success at this point! There is a lot more to go over but I wanted to get the bare basics working.   I am sure I will have more write ups on Swagger going over POST, PUT, DELETE, using parameters having your code auto generate a swagger api document, etc. but for today I think this is enough.



References
[1]  Swagger main page 
       Visited 6/2013
[2]  Swagger Demo Page 
       Visited 6/2013
[3]  Swagger github repository 
       Visited 6/2013
[4]  Basic HTTP Authentication With Nginx
       Visited 6/2013
[5]  HttpAuthBasicModule
       Visited 6/2013
[6]  Password protect your entire site with Nginx
       Visited 6/2013
[7]  Swagger resource listing
       Visited 6/2013
[8]  Swagger: API Declaration
       Visited 6/2013
[9]  How can multiple response content types and parameter content type be set?
       Visited 6/2013
[10]  Fiddler main website
       http://fiddler2.com/

       Visited 6/2013 

6 comments:

  1. Very helpful. Exactly what I was looking for.

    ReplyDelete
  2. Perfect ! If you know how to install swagger editor on swagger, can you also add it please ?

    ReplyDelete
  3. I agree with Jerry very helpful, thank you very much!

    ReplyDelete
  4. i am not understand. please improve your answer

    ReplyDelete