Part 2. Express Routing with Open API 3.0 and Swagger UI/Editor
In the previous part of the tutorial, we have covered initial project setup, let’s move on and make it work as REST API via HTTP. If you just start from this part, you can clone the sources by:
$ git clone https://github.com/losikov/api-example.git
$ cd api-example
$ git checkout tags/v1.0.0
$ yarn install
Hello World HTTP Web API Applicaiton
Install express npm, and its TypeScript definitions:
$ yarn add express @types/express
When we develop with TypeScript, it requires TypeScript definitions. Sometimes the definitions are delivered with a package itself. Sometimes you need to install an additional package with the definitions as we did with @types/express above. And rarely, defintions don’t exist. In this case we can define them ourselves, as I’ll show later in this part how to do it for swagger-routes-express package.
To browse a project and edit files I use Visual Studio Code.
Create server.ts file in src/utils folder with the following content:
Update src/app.ts file:
Let’s run the server now:
$ yarn dev
yarn run v1.22.4
$ ts-node ./src/app.ts
Listening on http://localhost:3000
And in browser you’ll get:
Let’s go ahead and define APIs entry points with Open API 3.0.
Open API 3.0 Integration
For the REST API description and for documentation purpose I love Open API 3.0 format, Swagger Editor, and Swagger UI (Viewer).
Create config folder in the root of the project and save openapi.yml file:
The config defines a root path /api/v1 with a single entry point GET /hello with an optional parameter name in the request query and a json response as { “message”: string }.
Swagger Editor
To edit the Open API config in Yaml format, you need an editor, which can validate your input and give a visualize your API.
Unless you often work offline, or confidentiality is your concern, you can use online version of the editor. I always use a local version of the editor which I run with docker, an application level virtualization container.
To run an offline version of the editor:
- Download and install the docker.
- Create scripts folder in the root of the project, and scripts/open_swagger_editor.sh file in it.
- Give executable permissions to the file:
$ chmod +x scripts/open_swagger_editor.sh
4. Copy the following code into the file:
5. Create common.sh in the same scripts folder and copy into the file:
You can run the editor now:
$ ./scripts/open_swagger_editor.sh
Give it a time if you run it for the first time, as it downloads the docker image. The script will open a browser:
Notes: Unfortunately, I didn’t find a way to make the editor to open, edit and save the local config file. That’s why, if I need to edit, I copy the config file content and paste it to the editor. When I’m done with the editing, I copy, paste it back and save. There’s no such issue with the viewer.
Swagger UI (Viewer)
The viewer is beneficial specially to the client developers. To run it:
- Download and install the docker.
- Create scripts folder in the root of the project, and scripts/open_swagger_ui.sh file in it.
- Give executable permissions to the file:
$ chmod +x scripts/open_swagger_ui.sh
4. Copy the following code into the file:
5. Create common.sh file from the previous Swagger Editor section, if you haven’t done so yet.
Run the editor:
$ ./scripts/open_swagger_ui.sh
Give it a time if you run it for the first time, as it downloads the docker image. It will open the browser:
Integration with Express
Now, let’s feed config/openapi.yml file to an express server.
First, install the required packages:
$ yarn add connect express-openapi-validator swagger-routes-express validator yamljs @types/validator @types/yamljs
Not all JavaScript packages have TypeScript declarations. swagger-routes-express is one of them. Compiler won’t work without the declaration file. We can fix it. Create a folder types/swagger-routes-express in the root of the project, and file index.d.ts with this content:
There is a property operationId with the value hello in GET /hello definition in openapi.yml. hello is a name of the function which will be called when GET /api/v1/hello request comes. Let’s implement this function.
Create a folder src/api/controllers and greeting.ts file in it with the following content:
Create index.ts file in the same folder:
This file is an entry point of all the controllers.
Finally, update src/utils/server.ts to load openapi.yml, make express to use API validator, and connect the controllers to corresponding HTTP requests:
If you run the server now, you will see a snapshot of the definition file, and connected routes:
$ yarn dev
yarn run v1.22.4
$ ts-node ./src/app.ts
{
info: {
name: 'Backend API example',
description: 'Backend API example API declaration',
version: '1.0.0'
},
paths: { get: [ '/api/v1/hello' ] }
}
get: /api/v1/hello : hello
Listening on http://localhost:3000
If you open in browser http://localhost:3000/api/v1/hello?name=Alex, you will get this:
If you try to pass an invalid parameter http://localhost:3000/api/v1/hello?ame=Alex, the backend will return an expected error:
Authentication with Open API 3.0
Express can easily handle which requests require authentication from openapi.yml config. Open API 3.0 supports HTTP authentication schemes (Authorization header), API keys, OAuth 2, and OpenID Connect. For our example, we’ll implement Bearer Authentication. Using Swagger Editor, described in the previous part, modify openapi.yml config and add one more /goodbye path following /hello in paths:
Where:
- schema — we reuse HelloResponse from GET /hello.
- operationId: goodbye — we’ll implement goodbye function in controllers to make it handle GET /goodbye requests.
- security property specifies a list of authentications for the request. In our case, it is a single bearerAuth. Add the definition to components section, where we have the schemas definition already:
After you are done with the modifications, you should get:
Pay attention to the lock button on the right side of the view next to GET /goodbye header.
Copy and paste your updates from the Swagger Editor into config/openapi.yml and save it. To modify the Open API document, I always use the Editor as it has Open API 3.0 code suggestion/completion and catch the errors.
Let’s implement the authentication itself. We’ll create our first user service for that. The purpose of the services is to process requests logic decoupled from express HTTP logic. Create src/api/services folder and user.ts file in it with the following content:
The auth function returns a promise with success: false/true status, and userId value if the authentication passed successfully. For now, we hardcode a single bearerToken successful value and connected to it userId. In the Redis/MongoDB part coming soon we will fix it.
We’ll call our service auth function implemented above from user controller. Create src/api/controllers/user.ts file with this content:
In line 7 of the snippet, we get Authorization HTTP header field and pass it to our auth service function in line 8. When we get a response from auth service, in other words when promise is resolved, if authorization is successful (line 10), we store userId (lines 11–13) and pass a control to the next express middleware with next() call (line 14). If authorization failed, we return a response with 401 HTTP status code (line 16) using writeJsonResponse helper function. Create src/api/utils/express.ts file, and insert the following content with the function implementation:
To make user controller to be visible to express, add export to src/api/controllers/index.ts:
export * from './greeting'
+ export * from './user'
Finally, you need to tell express about the user controller with auth function. Update src/api/utils/server.ts file and add security property to the connector options:
We are done with authentication. Let’s update greeting controller to add goodbye function which will be called on /GET goodbye based on our OPEN API config. As I use writeJsonResponse in both hello and goodbye function now, I provide a full snippet:
Time to try it! If you have any issues, check out the sources from a git repo.
If you make GET /goodbye request with a valid Bearer token, hardcoded fakeToken, you will get 200 and “Goodbye, fakeUserId!”:
If you send an invalid token, you will get 401:
If you don’t specify Authorization header field at all, it will return a request validation error with 401 status code, while GET /hello still works as expected without Authorization header:
Open API 3.0 schema gave a much better visibility on all stages of development, from initial API design drafts to a final client implementation.
You can download the project sources from git https://github.com/losikov/api-example. Git commit history and tags are organized based on the parts.
In the next part of the tutorial, we’ll learn how to make express to log requests/responses and pass variables to Node.js application for differetn environments (prod, dev, etc).