This project serves as a demo for an API CodeFlow approach to API service implementation. This entails the following three steps, repeated as the API evolves:
-
Design your API in OpenApi.
-
Generate stub code in Node.js (JavaScript).
-
Implement your business logic.
This demo will create a working API to manage a database of live pets for a pet store. (No animals have been harmed in the creation of this demo.)
A custom code generator was built especially for this demo, to show how you can tailor code generation to meet your specific needs. This code generator is not complete, in the sense that it lacks a number of useful features. However it does actually work, and it includes all of the following:
-
Automatic generation of controllers and handlers for all operations defined by the API. The controllers are fixed code that are not intended for user editing and will be overwritten during regeneration. They serve as bridges between the service framework and the handlers. Handlers are where business logic belongs. Handlers are never overwritten by the generator.
-
Controllers and handlers are organized by tags to simplify code management.
-
Automatic integration of Swagger-UI, with options to re-route Try-It-Out requests to your running service, overriding server definitions in the model.
-
A very simple built-in database with basic CRUD capabilities.
The demo proceeds in five phases (plus some initial set-up). There are branches defined for each phase, which you can use to quickly bring your workspace up to that point of the demo, rather than manually working through each step.
💡
|
If you have checked out a branch for the end of one phase and then worked through the next phase manually, you will almost certainly have files in your working tree that will block an attempt to simply check out the branch for the next phase. To proceed, clean out your working tree with:
If you have also modified non-ignored files, you will also need to do this:
|
The phases are:
-
Setup - install and launch RepreZen API Studio, and install Nodeclipse from the Eclipse Marketplace, so you’ll be able to work with Node.js code.
-
Obtain the custom generator from the demo repository and activate it in your workspace.
-
Design - Create one of the standard OpenAPI v3 example models available in RepreZen API Studio.
-
Generate - Prepare a Node.js project for the service implementation, and use the code generator to populate it.
-
Implement - Write your business logic into the generated stubs.
-
Iterate (Design ⇒ Generate ⇒ Implement) in order to add a new method to the API
So let’s dig in!
If you don’t already have RepreZen API Studio installed, you’ll want to visit the RepreZen website and sign up for a free trial. If you’re already an Eclipse IDE user, you may want to consider installing API Studio from Eclipse Marketplace, rather than using the stand-alone installer offered in the sign-up process. Installation options are explained here.
Once you’re up and running, you should install Nodeclipse from Eclipse Marketplace, which you can find on the Help menu. Follow the instructions, and after a restart you’ll have added robust support for Node.js projects to the product.
|
There is an unimportant conflict involving a particular feature required by Nodeclipse and already present (in a different version) in API Studio. During installation you’ll be informed of this asd presented with a recommendation as to how to proceed. You should accept the recommendation. |
Your final setup step, if you haven’t done it already, is to clone this project to a local working directory on your machine.
git clone [email protected]:RepreZen/API-Codeflow-Node.js.git
💡
|
The end state of this phase is captured in branch 01.gentemplate .
|
In this step you will change your workspace to one provided in the demo project, having checked out a branch that includes the generator as a workspace project (in contrast to the numerous pre-packaged geneartors that are available out of the box in API Studio).
Follow these steps:
-
In your demo project working directory, checkout the
01.gentemplate
branch.git checkout 01.gentemplate
-
In API Studio, use File → Switch workspace → Other… and navigate to your working directory, and then into the
workspace
folder you’ll find there. -
API Studio will restart after you click the Launch button.
-
Use File → Import…, and in the resulting dialog select the Maven / Existing Maven Projects option and click Next. In the next panel, use the Browse button to locate your working directory and click Next. You should check tne NodeGenTemplate project, and click Finish.
-
There will be small delay while this project builds for the first time on your machine.
|
In some cases, the initial build will not work correctly, due to a bug that we hope to
remedy shortly. You will know this from red error markers on some of the folders inside the the main
NodeGenTemplate project folder. If you see them, right-cliick on that project folder, select Run
As → Maven build…, and then type compile into the Goal field before pressing Run.
|
💡
|
The end-state of this phase is captured in branch 02.petstore .
|
We won’t actually design a model here. Instead, we’ll just use one of the OpenApi3 models available from the API Studio Examples Wizard.
Follow these steps:
-
Click on the drop-down arrow of the New tool in the toolbar, just under the File menu.
-
Select RepreZen Examples to open the Examples Wizard.
-
Click on the OpenAPI v3 tab.
-
Select the Expanded Pet Store (v3) example, and press Finish.
-
You should see a new project in your workspace, and the example model file itself will automatically open in an editor.
-
Browse through the model briefly to familiarize yourself with its operations and other components.
💡
|
The end state of this phase is captured in branch 03.service .
|
This is where we’ll generate code for the model we created, in phase 2. We’ll arrange for the generated files to land directly in a Node.js project that we will set up for that purpose. Later regeneration cycles will all continue to feed into that project.
Follow these steps:
-
Right-click in the Project Explorer pane and use New → Node.js Project to bring up a wizard.
-
Type
PetStoreService
for the Project name. -
Select the none/empty template, then press Finish. A new project appears in your workspace.
-
In your model project, locate the
petstore-expanded.yaml
file in themodels
folder, and click on it. -
Click on the Create a New GenTarget button in the toolbar, just to the left of the Generate button/menu.
-
Type "node" in the resulting dialog’s search box, and you should see our NodeGenTemplate generator. Select it and press Finish. A new GenTarget is created in your project, and the
.gen
file that describes it opens in an editor. -
Make and save the following changes in this file:
-
Near the top, change the value for
relativeOutputDir
to../../../../PetStoreService
. This is what will cause generated files to flow directly into the project we just created. -
Set
pathPrefix
to/api
, to align with the path prefix listed in the first server defined in our model. This will cause the running service to properly recognize and route requests sent from Swagger-UI. -
Set
swaggerUIPath
toapi-ui
. The default,/api
, clashes with thepathPrefix
that is dictated by the server definition in our model. (Of course, we could also just change that server definition to use a different path prefix, or just remove it altogether.)
-
-
Run the generator, by clicking on the big
Generate
button in the toolbar. (Since we’ve been actively editing the.gen
file for the NodeGenTemplate generator, the menu should show that as the generator to run. If not, click instead on the small arrow to the right, and select NodeGenTemplate from the list of targets.) -
Even though the service project files are now present, they will not appear in Project Explorer until you cause a refresh of the project files. Right-click on
PetStoreProject
in Project Explorer and then select Refresh to do this.
💡
|
We’ll be doing this a couple more times in later phases. |
💡
|
The end state of this phase is captured in branch 04.implement .
|
Now it’s time to write the code that will implement the business logic of our API service.
You should only need to touch files in the handlers
folder of the PetStoreService
project. In
this case there’s only one file - Untagged.js
. Normally, there could be several files here, named
after the tags defined in the model. When operations are grouped using tags, this allows the overall
implementation code base to be split into more manageable pieces. In our example model, tags are not
used, so all the handlers ended up in a single Untagged
source file.
If you’re reasonably proficient with Javascript, Node.js and Express.js, you may want to take a
crack at this yourself. But you can also skip forward by chekcing out the necessary files from
branch 04.implement
of the demo repo. In that case you may want to take a look at the before and
after images of handlers/Untagged.js
, just to get a sense of what’s going on.
To check out final the implementation from the repo, use this command, from the root of your working tree:
git checkout origin/04.implement -- workspace/PetStoreService
You’ll need to refresh the PetStoreService
again to see the changes in API Studio.
The basic design of the handlers goes like this:
-
Each controller method implements the logic for a single operation defined in the model.
-
The methods are named after operation ids if they exist. Otherwise they’re a combination of the operation’s path string (up to but not including the first path parameter) and the operation’s HTTP method. Name collisions are disambiguated with trailing integers.
-
Each method is declared with a parameter list that corresponds to the operation’s declared parameters in the model. If any path-level parameters are inhereted by this operation, they follow the operation’s own parameters. If the operation defines a
RequestBody
there will be a finalbody
parameter. -
The handler is expected return a new
Promise
that has either been resolved to a value for the response payload, or rejected with an error object that should havecode
andmessage
properties. -
The non-error response can also be an object with
code
andvalue
properties, in which case thecode
value will be used for the HTTP status code, and thevalue
property will be used for the payload. -
If the response payload is
undefined
, no response will be provided, and the default status code will be 204. Otherwise the default status code will be 200. -
All payloads - including the error objects - are sent as JSON values.
-
Each handler makes calls to validators, one for each parameter. Stubs for the validators are also provided, after all the handlers.
-
Validators should throw an error object if validation fails.
-
Each validator that does not throw must return a final value for the parameter it checked. This is where, e.g. a string value from a query parameter is converted into an integer after testing that it’s syntactically valid.
If you want to run your implementation, you can follow these steps:
-
Right-click on the
package.json
file in the service project, and select Run As → npm install. You’ll only need to do this again if you change the file or remove thenode_modules
directory. -
Refresh the
PetStoreService
project again. This is needed in order for the results of the build to become available in the project, since the build itself is carried out in a separate process. -
Right click on
app.js
in the service project, and select Run As → Node Application. You should see a start-up message in a console pane that makes itself visible. -
Visit http://localhost:3000/api-ui in a web browser. You should see Swagger-UI displaying your model. The "Try It Out" buttons will work, and requests will be directed to your running instance, regardless of the server definitions in the model itself.
💡
|
The end state of this phase is captured in branch 05.patch .
|
Missing from the API model is an operation that allows modification of selected properties of
a pet. In phase 5 we add a patch
operation to the /pets/{petId}
path to supply this
capability. The steps are:
-
Add the operation to the model file,
petstore-expanded.yaml
in the model project. -
Rerun the generator. Everything but the handler files will be refreshed and will reflect the additional operation.
-
Add a handler for the new patch method to the handler file (the corresponding controller will already be updated).
To check out a working implementation from the demo repository, use the following command (then
refresh the PetStoreService
project again):
git checkout origin/05.patch -- workspace/PetStoreService 'workspace/Expanded Pet Store (v3)/models'
You’ll find that another method has been added to the handlers/Untagged.js
source file.
And that’s it. At that point you should be able to re-launch the application and make use of the nifty new patch method.
|
If you attempt to re-launch the app and see an error message indicating that the port is already in use, it’s because your prior launch is still running and still listening on port 3000. To terminate that launch, open the Console view (use Window → Show view → Other… and then select the General/Console view and click Open.) Near the right end of that view’s toolbar, open the Display Selected Console menu, and select a console labled PetStoreService… that is not marked as terminated. You’ll then see a square red toolbar button that you can use to terminate the launch. At that point you should be able to successfully re-launch the service app. |