After you have installed Visual Studio Code, then go ahead and install the Thunder Client extension. This will allow you to test your APIs from within VS Code.
Access to an IBM i
If you are doing this workshop as part of a hosted lab, then a user profile and system will be provided to you. If you’re using your own system, then ensure SSH is running on your system. Check out the requirements.
The main goal of this workshop is to understand how APIs are created with Node.js. We will create routes that do CRUD against a Db2 for IBM i database.
Understanding Local Development
For the workshop, we are going to develop a Node.js/TypeScript application on your local machine. A standard way to develop Node.js apps if to develop locally, or in a container first, before then deploying to another server (like IBM i).
Getting and running the template
A Node.js API template, built specifically for using TypeScript, creating APIs with express, and using ODBC to connect to IBM i, is provided as part of this lab.
Select ‘Use this template’ and ‘Create a new repository’. Fill out your new repository information and when it has been created, clone it to your local device.
Ether use the same name as the template, or give it your own unique name. Either way, press ‘Create Repository’
Clone the repository in VS Code
The next step is to clone your template onto your local machine. The easiest way is to do this through Visual Studio Code. In the Source Control view, there is a ‘Clone repository’ button, in which you can lookup a repository. Select (or use Enter) ‘Clone from GitHub’.
In the new quickpick, enter your GitHub username, followed by a slash, and the name of the template repository you created.
After you select your repository, it will ask if you want to ‘Open’ or ‘Open in new window’. Either works ok!
Create the .env file
For the purpose of development, we are going to use a .env file to store credentials to access the database. This file is not committed to the repository, so it is safe to store sensitive information in it.
Create a copy of .env.sample and rename it to .env. This file is in the root of the project.
Replace the values in the .env file with your own IBM i credentials. If you’re taking this as part of a pre-built lab, then the credentials will be provided to you.
Install the dependencies
The next step is to install the dependencies. Open a terminal in VS Code and run npm install (or npm i for short!). Use Control + ` (backtick) to open the terminal.
Start debugging
The template comes with a launch configuration for debugging. Press F5 to start the debugger, or select the ‘Run and Debug’ button in the sidebar as shown in the screenshot. This will start the application and attach the debugger to it.
You can access the site by using localhost, followed by the port number (PORT variable) defined in the .env file.
Building your first route
Enable the test route
To test that we have a valid database connection, we can uncomment the test route in src/routes/index.ts. This route will execute a simple select statement against the database and return the results.
Debugging the route
To test the route, we can use the debugger. Start the debugger as we did earlier, and then open a browser to localhost:3000/test. This will hit the route and return the results. You can also use Thunder Client to test the route. Under the Thunder Client view, use the ‘New Request’ button, which opens a brand new tab where you can enter the address you are testing.
Understanding our API
For the remainder of the lab, we are going to build an API according to a specification. We defined the specification inside of this gist. At a first glance, it’s a big, ugly, YAML document, but it’s actually quite simple.
This YAML document is a specification for an API. It defines the routes, the methods, and the responses that are expected. This is a common way to define APIs, and it’s called OpenAPI. Sometimes you may hear it called Swagger, but that’s just a brand name for the same thing.
Visualising the APIs
To visualise the APIs, we are going to use a tool called Swagger UI. This is a web-based tool that reads the OpenAPI specification and displays it in a nice, human-readable format.
As you can see from the screenshot, we have a few routes defined. We are going to build these routes in the next section.
Building the first route (employees)
Here we are going to build the first route, namely /employees. This route will allow many things: listing, updating and creating (i.e. CRUD) employee in our database.
Adding the new route
We need to do two things:
Create a new file, src/routes/employees.ts, with the following content:
Update src/index.ts to include the new route:
Anything route defined in this new employees.ts file will be prefixed with /employees. For example, we defined a route /, then it will be /employees/.
Testing the route
To test the route, we can use the debugger. Start the debugger as we did earlier, and then open a browser to localhost:3000/employees (or use Thunder Client). This will hit the route and return the results.
Matching the specificiation
The specification defines that the route /employees should return a list of employees. We can do this by querying the database and returning the results. But, we need to make sure that the results are in the correct format - an array of objects with specific key names.
If you expand ‘GET /employees’ in the Swagger Editor, you can see from the example response that it should return an array of objects with specific keys. We need to make sure that our route returns this format. Let’s start by building an SQL statement that matches the employees array.
We use correlation names to match the keys in the JSON object since Db2 for i columns are uppercase by default.
When we run this SQL statement, we will get an array of objects that match the specification. We can then return this array in the response.
Let’s update the root route in src/routes/employees.ts to execute and return the result set of this statement.
We can test the route by calling localhost:3000/employees in the browser or Thunder Client.
But, notice that our result JSON doesn’t match the specification. We still need to add the length key to the response. We can do this by wrapping the result set in an object that contains the length key.
Now, when we test the route, we should see the correct response.
Building a second route (departments)
Much like when we built the /employees route, we are now going to do the same with /departments. This route will allow us to list, update and create departments in our database.
Adding the new route
Let’s start by doing two things:
Create a new file, src/routes/departments.ts, with the following content:
Update src/index.ts to include the new route:
Note that in the /departments result set, we already created it to match the ‘GET /departments’ specification. We can test the route by calling localhost:3000/departments in the browser or Thunder Client.
Calling an RPGLE program (optional)
If you took part in the Git and RPGLE workshop and created the DEPTS SQL RPGLE program, then you know that the program returns an SQL result set. We can call this program from our Node.js application and return the result set too! Simply replace the select statement with a call to the program.
If you compare the programs result set to the required output of ‘GET /departments’ in the Swagger Editor, you will see that they do not match. The columns from the program are incorrect. Here is a challenge for you, either:
Update the program to return the correct columns. (recommended method!)
Update the TypeScript code to map the columns to the correct keys. (Perhaps departments.map(...) will do the trick, but slower!)
Getting a specific row/record
The specification also defines that we should be able to get a specific department by its ID. We can do this by adding a new route to the /departments route. This route will take a parameter, the department ID, and return the department with that ID.
Add the following route to src/routes/departments.ts, below the root (/) route.
This route will take a parameter, id, and return the department with that ID.
If the department is not found, we will return a 404 status code.
Now we have two routes in the /departments route:
/ - Returns all departments
/:id - Returns a specific department, where ID is the department number
We can test the new route by calling localhost:3000/departments in the browser or Thunder Client, and then localhost:3000/departments/xxx where xxx is a department number.
Building a PATCH route
The specification defines that we should be able to update a department. We can do this by adding a new route to the /departments route. This route will take a parameter, the department ID, a body with properties to update.
Adding the patch route
Below is the patch route which can be used to update specific departments.
There is a lot of logic happening in this code.
We validate that the request has a body and a department ID.
We map the input object to the columns in the database.
We update the table with the new values.
We get the updated department and return it.
This new route can only be called with a PATCH request, which means we cannot test this in the browser very easily. We can test the new route by calling localhost:3000/departments/xxx in Thunder Client, with a PATCH request and a body with the properties to update.
Adding the OpenAPI validator
We have built the routes according to the specification, but we have not validated that the routes match the specification. We can do this by adding an OpenAPI validator to our application. We can embed the OpenAPI specification into our application and use a library to validate the routes.
Adding the validator
We need to do two things:
Run npm i express-openapi-validator in the VS Code terminal to install the OpenApi validator for ExpressJS package.
Create src/spec/openapi.yaml and paste [the OpenAPI specification from the Gist] into it.
Following that, we need to follow the simple OpenApi Validator tutorial to add the validator to our application. The following changes are to be made to src/index.ts:
Testing the validator
The validator will now check that the inputs and outputs of API calls match the specification. If they do not, it will return a 400 status code with an error message. We can test this by calling the same ‘GET /departments/:id’ API with different parameters: