Sync automatically your ExpressJs server with Azure Api Management Service.
import { APIMify } from "apimify";
import { router } from "./routes";
new APIMify({
express: router,
resourceGroupName: "resourceGroup",
serviceName: "serviceName",
apiId: "api"
}).sync();
- Installation
- Usage
- Configuration Options
- Operation Configuration
- Operation Policies
- Warning!
- License
This is a Node.js module available through the npm registry:
$ npm install apimify
import { APIMify } from "apimify";
import express from "express";
const app = express();
app.get("/", (req, res) => res.json({hello: "world"}).status(200));
app.post("/", (req, res) => res.json({hello: req.body}).status(200));
new APIMify({
express: app,
resourceGroupName: "resourceGroup",
serviceName: "serviceName",
apiId: "api"
}).sync();
import { APIMify } from 'apimify';
import express from 'express';
const app = express();
app.get('/', (req, res) => res.send("Hey!"));
new APIMify({
express: app,
resourceGroupName: 'resourceGroup',
serviceName: 'serviceName',
apiId: 'api',
auth: {
username: "username",
password: "qwerty",
subscription: "AzureSubscription"
}
}).sync();
import { APIMify, Metadata } from "apimify";
import express from "express";
const app = express();
app.get(
'/',
Metadata.set({ displayName: 'Greeting' }),
Metadata.set({ tags: ['useless endpoint'] }),
(req, res) => res.json({ hello: 'world' }).status(200)
);
app.post(
'/',
Metadata.set({
displayName: 'Personal Greeting',
description: 'This endpoint will return a custom made JSON object based on the POST payload. Cool, hah?'
}),
Metadata.set({
responses: [
{
representations: [
{ contentType: 'application/json', sample: '{ hello: "John Doe" }' }
],
statusCode: 200
}
]
}),
(req, res) => res.json({ hello: req.body }).status(200)
);
new APIMify({
express: app,
resourceGroupName: 'resourceGroup',
serviceName: 'serviceName',
apiId: 'api'
}).sync();
import { APIMify, rateLimit, Policy } from 'apimify';
import express from 'express';
const app = express();
app.get('/', (req, res) => res.send("I have no limits!"));
app.get('/rate-limited', rateLimit({"calls": 100, "renewal-period": 60}), (req, res) => res.send("I do :)"));
app.use(
Policy.create(`
<validate-jwt header-name="Authorization" require-scheme="Bearer">
<issuer-signing-keys>
<!-- You can use named values -->
<key>{{jwt-signing-key}}</key>
</issuer-signing-keys>
<audiences>
<!-- You can use any expression you need - It's simply being passed to APIM -->
<audience>@(context.Request.OriginalUrl.Host)</audience>
</audiences>
</validate-jwt>
`, "inbound")
);
app.post('/authenticated', (req, res) => res.send("Do you have a JWT?"));
app.delete('/jwt-required', (req, res) => res.send("You Betcha!"));
new APIMify({
express: app,
resourceGroupName: 'resourceGroup',
serviceName: 'serviceName',
apiId: 'api',
apiVersion: "v2"
}).sync();
Returns an empty promise when sync is complete.
configuration
:
express
: An express instanceexpress()
or a router.auth
: (optional) Azure authentication:{}
- will try to authenticate using Azure CLI. This is the default.{ subscription: string }
- will initiate an interactive authentication{ subscription: string, credentials: AzureCredentials }
- you can use any authentication method in@azure/ms-rest-js
, and pass the credentials.{ subscription: string, username: string", password: string }
- authenticating using username and password (won't work if MFA enabled).
resourceGroupName
: A string with the name of the resource group.serviceName
: A string with the name of the API Management service.apiId
: A string with the API identifier. If you want to use a specific revision you can append;rev=
and the revision numberapiVersion
: (optional) A string with the API version.basePath
: (optional) A string with a path to append to the express routes.breakOnSamePath
: (optional) Set to true if you want an error to be thrown in case of path overlap, for example, in case of a route with/user/:id(\d*)
as path, and another route with the same method and/user/:name([A-z\-]*)
as path - APIM can't distinguish between the two. Default: false.generateNewRevision
: (optional) Set to true if you want to create a new revision on sync. Default: true.makeNewRevisionAsCurrent
: (optional) Set to true if you want to mark the revision as current. Default: matchgenerateNewRevision
.logger
: (optional) A logger. Defaults toconsole
logLevel
: (optional) An object with log level as keys and boolean as values indicating wether to log a level or not. Default: log all levels.
Returns an express middleware that you can use just like any other middleware.
configuration
:
description
: (optional) Description of the operation. May include HTML formatting tags.request
: (optional) An entity containing request details.responses
: (optional) Array of Operation responses.displayName
: (optional) Operation Name.operationId
: (optional) Operation identifier within an API. Must be unique in the current API Management service instance.tags
: (optional) Array of tags
Returns an express middleware that you can use just like any other middleware.
XML
: A string with valid APIM policy XML. You can use named values and any other expressionLocation
:inbound
: The XML will be applied to the request.backend
: The XML will be applied before the request is forwarded to the backend service.outbound
: The XML will be applied to the response.on-error
: The XML will be applied if there is an error condition.
checkHeader(configuration, 'inbound' | 'outbound')
: configuration and informationrateLimit(configuration)
: configuration and informationrateLimitByKey(configuration)
: configuration and information
I'll be adding more soon
Running sync()
will delete all existing operations if they don't have an express route - so be carful! Start by setting generateNewRevision
to true and makeNewRevisionAsCurrent
to false, check if everything works, make sure that the deletion of the operations don't affect your API, look for policies you might forgot, etc. If all looks OK, you can set makeNewRevisionAsCurrent
to true and forget from the APIM portal.
This is a slow process, so I recommend using it as a CI/CD step and not on every app initiation - place APIMify in a separate file from the one initiates the express server and call it only when you need it.