Building a Node.js E-Commerce API for noobs — Swagger
This time I’m going to get my hands really dirty!
Since I’m building an API that’s meant for more than one frontend application I think it’s a good idea to use best practices to document and develop it…
So far..
.. I have tested what impact it has when authenticating against Moltin everytime I’d like to call Moltin API for products, reviews, checkout, whatever; not that big of an issue because Moltin is blazingly fast! Though, I don’t think it’s supposed to be used this way? (Maybe someone can give me an answer to that question)
And created a basic webserver using express.
But what’s about an real API
Real API, real documentation, real swagger…
Swagger is the real deal!
I think.
Never worked with it.
But I want to make use of the AWS API-Gateway later.. So I’m going to put it into a schema that can be imported by the API-Gateway!
Code
First of all I’ll get rid of the previously used app.js and create a new repo, install the Swagger CLI and create a swagger project!
$ mkdir ecommerce-api &&
$ cd $_ &&
$ npm i -g swagger &&
$ swagger project create ecommerce-api && #I choose express
$ swagger project start ecommerce-apiVoilà! This tells me I can run a test to the newly create API from a new terminal window:
$ curl http://127.0.0.1:10010/hello?name=ScottYeah! API up and running!
Meanwhile I’ll open the swagger-editor in chrome with
$ swagger project edit ecommerce-apiLooks useful.
When opening the project in an editor, I find a prebuilt structure which makes it pretty easy to follow the best practices in terms of folder structure in Swagger!
Also I got an app.js that looks pretty much the same as the one I created before…
'use strict';var SwaggerExpress = require('swagger-express-mw');
var app = require('express')();
module.exports = app; // for testingvar config = {
appRoot: __dirname // required config
};SwaggerExpress.create(config, function(err, swaggerExpress) {
if (err) { throw err; }// install middleware
swaggerExpress.register(app);var port = process.env.PORT || 10010;
app.listen(port);if (swaggerExpress.runner.swagger.paths['/hello']) {
console.log('try this:\ncurl http://127.0.0.1:' + port + '/hello?name=Scott');
}
});
I don’t need the if statement on the bottom so I just remove it.
Basic…
What’s actually more fun is the Swagger file in /api/swagger/swagger.yaml => looks crazy to me; might be because I’m not used to working with .yaml files.
swagger: "2.0"
info:
version: "0.0.1"
title: Hello World App
# during dev, should point to your local machine
host: localhost:10010
# basePath prefixes all resource paths
basePath: /
#
schemes:
# tip: remove http to make production-grade
- http
- https
# format of bodies a client can send (Content-Type)
consumes:
- application/json
# format of the responses to the client (Accepts)
produces:
- application/json
paths:
/hello:
# binds a127 app logic to a route
x-swagger-router-controller: hello_world
get:
description: Returns 'Hello' to the caller
# used as the method name of the controller
operationId: hello
parameters:
- name: name
in: query
description: The name of the person to whom to say hello
required: false
type: string
responses:
"200":
description: Success
schema:
# a pointer to a definition
$ref: "#/definitions/HelloWorldResponse"
# responses may fall through to errors
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
/swagger:
x-swagger-pipe: swagger_raw
# complex objects have schema definitions
definitions:
HelloWorldResponse:
required:
- message
properties:
message:
type: string
ErrorResponse:
required:
- message
properties:
message:
type: stringShoutout to whomever created this CLI! Thank you for the comments ;)
I’ll just copy the /hello part from the file and paste it right between “$ref” and “/swagger:” rename the duplicate to /hey and paste it
...# the new path
/hey:
# binds a127 app logic to a route
x-swagger-router-controller: hello_world
get:
description: Returns 'hey' to the caller
# used as the method name of the controller
operationId: hey
parameters:
- name: name
in: query
description: The name of the person to whom to say hello
required: false
type: string
responses:
"200":
description: Success
schema:
# a pointer to a definition
$ref: "#/definitions/HelloWorldResponse"
# responses may fall through to errors
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
...
Looking at the browser window where the swagger editor is still open I can see my new route!! Yeah, nice!
It’s probably not going to work before I create the function with the operationId = “hey” in the /api/controllers/hello_world.js (here I also found the operationId =” hello”)
...module.exports = {
hello: hello,
hey: hey
};...function hey(req, res) {
// variables defined in the Swagger document can be referenced using req.swagger.params.{parameter_name}
var name = req.swagger.params.name.value || 'stranger';
var hey = util.format('Hello, %s!', name);// this sends back a JSON response which is a single string
res.json(hey);
}
So I just duplicated the already existing hello function and renamed it to hey..
I’d like to test it!!
$ curl http://localhost:10010/hey?name=Bob # returns 'Hello, Bob!'Nice! Working as expected!
That was easy!
Bring Moltin and my middleware back!
Alright, so now I got a really professional looking API ready to roll!
Only thing left now is to bring back the functionality I had in the Express Code before!
I create a new file in the controllers folder and call it moltin_requests.js
// require the util module thats backed into node
const util = require('util');// same same
const moltin = require('moltin');
const Moltin = moltin.gateway({
client_id: 'BlaBla_client_id',
client_secret: 'BlaBla_client_secret'
});function moltin_auth(req, res) {
Moltin.Authenticate()
.then(response => {
console.log('authenticated', response);
res.json(util.format(response));
})
}// export the authentication function
module.exports = {
moltin_auth
};
Also I need to update the swagger file to rename the path, it’s controller and the function that get’s triggered.. Should probably also create a new response schema:
...
/moltinAuthenticate:
# binds a127 app logic to a route
x-swagger-router-controller: moltin_requests
get:
description: Returns the Moltin API response after authenticating
# used as the method name of the controller
operationId: moltin_auth
responses:
"200":
description: Success
schema:
# a pointer to a definition
$ref: "#/definitions/MoltinAuthResponse"
# responses may fall through to errors
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
...
# complex objects have schema definitions
definitions:
HelloWorldResponse:
required:
- message
properties:
message:
type: string
MoltinAuthResponse:
required:
- authentication
properties:
authentication:
type: object
ErrorResponse:
required:
- message
properties:
message:
type: stringLet’s give it a shot!
$ curl http://localhost:10010/moltinAuthenticate # returns an objectLast thing I’m going to takle is the middleware thing.. which is basically the same as before!
moltin_requests.js
const util = require('util');const moltin = require('moltin');
const Moltin = moltin.gateway({
client_id: 'BlaBla_client_id',
client_secret: 'BlaBla_client_id'
});function moltin_auth(req, res) {
Moltin.Authenticate()
.then(response => {
console.log('authenticated', response);
res.json(util.format(response));
})
}// don't get too confused here.. this function is basically doing // the same as moltin_auth() but you'll get the point
function moltin_auth_middleware(req, res, next) {
Moltin.Authenticate()
.then(response => {
console.log('authenticated via middleware', response);
next();
})
}// export the middleware function
module.exports = {
moltin_auth,
moltin_auth_middleware
};
app.js
'use strict';const SwaggerExpress = require('swagger-express-mw');// require only the middleware function I just created
const { moltin_auth_middleware } = require('./api/controllers/moltin_requests');
const app = require('express')();const config = {
appRoot: __dirname // required config
};// tell express to use the middleware function as middleware
app.use(moltin_auth_middleware)SwaggerExpress.create(config, function(err, swaggerExpress) {
if (err) { throw err; }// install middleware
swaggerExpress.register(app);const port = process.env.PORT || 10010;
app.listen(port);
});module.exports = app; // for testing
No big changes!! Great :)
$ curl http://localhost:10010/hello
# this will now log the moltin response to the terminal running the # project and log 'Hello, Bob!' to the current one!I for sure should create a dedicated folder for middlewares.. just to keep things clean!
DONE!
Working awesome so far!!!
Next, I’m going to clean things up and create real functions an new routes!
