Skip to main content

Create Server

Your Client ID & Secret should be protected and kept confidential as all your files will be bound to your account. For a web application, keep it on your server. This section demonstrate how to prepare create a local development server.

Please review Environment Setup section for required software.

Setup Project

Create a new folder for your project, navigate to it in the command line, and initialize a new Node.js project:

mkdir designAutomationSample
cd designAutomationSample
npm init -y

Next, install all the Node.js dependencies we're going to use. In this case it will be the Express.js framework, an Express.js middleware for handling multipart/form-data requests, and finally the APS SDK:

npm install --save express express-formidable forge-apis

The "dependencies" in your package.json file should now look something like this (potentially with slightly different version numbers):

// ...
"dependencies": {
"express": "^4.17.1",
"express-formidable": "^1.2.0",
"forge-apis": "^0.9.1"
}
// ...

Application Config

Install packages

By default, a Node.js project is empty, so we need to install a few packages with npm install. Let's start with a basic express server, body-parser for JSON handling, multer for file upload and, of course, APS.

caution

Run one npm install at a time.

npm install express --save
npm install express-formidable --save
npm install multer --save
npm install cookie-session --save
npm install forge-apis --save
npm install autodesk.forge.designautomation --save
npm install body-parser --save
npm install form-data --save
npm install socket.io --save

The --save parameter indicates that the module should be included in the package.json file as a dependency.

Finally open the package.json and, inside "scripts", add "start": "node start.js", line. Now your folder should have a node_modules folder and your package.json should look like this

package.json
{
"name": "designAutomationSample",
"version": "1.0.0",
"description": "",
"main": "start.js",
"scripts": {
"start": "node start.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"autodesk.forge.designautomation": "^3.0.3",
"body-parser": "^1.19.0",
"cookie-session": "^2.0.0",
"express": "^4.17.1",
"forge-apis": "^0.9.2",
"form-data": "^4.0.0",
"multer": "^1.4.5-lts.1",
"socket.io": "^4.0.1"
}
}

The version number (e.g. forge-apis 0.9.2) may vary, it was the latest version when this tutorial was created.

Files and Folders

To create a new folder or file, right-click on the "Explorer" area on the left and select New Folder or New File.

Create a /routes/ folder for all server-side files and a /wwwroot/ folder for all client-side files.

At this point, you project should have the following structure

This file indicates to Visual Studio Code how we should run our project. Go to menu Run >> Add Configuration... and, in the Select Environment window that appears on the top, choose Node.js. In the /.vscode/launch.json file that is created, enter the following

launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/start.js",
"env": {
"APS_CLIENT_ID": "your id here",
"APS_CLIENT_SECRET": "your secret here"
}
}
]
}
note

It's important to define ID & Secret as environment variables so our project can later be deployed online. More on this later, in Deployment.

Server Setup

This file starts an express server. In the root folder, create a start.js file with

caution

File names are case-sensitive for some deployments, like Heroku. For this tutorial, let's use lower-case.

start.js
const app = require("./server");
const socketIO = require("./socket.io")(app);

let server = socketIO.http.listen(app.get("port"), () => {
console.log(`Server listening on port ${app.get("port")}`);
});

server.on("error", (err) => {
if (err.errno === "EACCES") {
console.error(`Port ${app.get("port")} already in use.\nExiting...`);
process.exit(1);
}
});

This file serves static files (e.g. html), and routes API requests. In the root folder, create a file named server.js with the following content.

server.js
const _path = require("path");
const express = require("express");
const cookieSession = require("cookie-session");
const config = require("./config");
if (!config.credentials.client_id || !config.credentials.client_secret)
return console.error(
"Missing APS_CLIENT_ID or APS_CLIENT_SECRET env variables."
);

let app = express();
app.use(express.static(_path.join(__dirname, "./wwwroot")));
app.use(
cookieSession({
name: "aps_session",
keys: ["aps_secure_key"],
maxAge: 60 * 60 * 1000, // 1 hour, same as the 2 legged lifespan token
})
);
app.use(
express.json({
limit: "50mb",
})
);

app.set("port", process.env.PORT || 8080);

module.exports = app;

In the root folder, create a file named socket.io.js with the following content.

socket.io.js
module.exports = (app) => {
const http = require("http").Server(app);
const io = require("socket.io")(http);
app.io = io;

let clients = 0;
io.on("connection", (socket) => {
clients++;
console.log("a client is connected");

// Whenever someone disconnects this piece of code executed
socket.on("disconnect", function () {
clients--;
console.log("a client disconnected");
});
});

return {
http: http,
io: io,
};
};

In the root folder, create a file named config.js with the following content.

config.js
// Autodesk Platform Services configuration
module.exports = {
// Set environment variables or hard-code here
credentials: {
client_id: process.env.APS_CLIENT_ID,
client_secret: process.env.APS_CLIENT_SECRET,
callback_url: process.env.APS_CALLBACK || process.env.APS_CALLBACK_URL,
},
scopes: {
// Required scopes for the server-side application
internal: [
"bucket:create",
"bucket:read",
"bucket:delete",
"data:read",
"data:create",
"data:write",
"code:all",
],
// Required scope for the client-side viewer
public: ["viewables:read"],
},
client: {
circuitBreaker: {
threshold: 11,
interval: 1200,
},
retry: {
maxNumberOfRetries: 7,
backoffDelay: 4000,
backoffPolicy: "exponentialBackoffWithJitter",
},
requestTimeout: 13000,
},
};

We are using the environment variables here. At the time of running our Express server, the values of these variables will be used to connect to APS.

Now create a common subfolder in the routes folder, and prepare a routes/common/oauth.js file that will actually request the access token from APS. This will be reused in other parts of this tutorial.

  • routes/common/oauth.js
oauth.js
const { AuthClientTwoLegged } = require("forge-apis");
const config = require("../../config");

// Tokens are auto-refreshed, keeping clients in simple cache
let cache = {};

// Since we got 3 calls at the first page loading, let's initialize this one now,
// to avoid concurrent requests.
getClient(/*config.scopes.internal*/);

/**
* Initializes a APS client for 2-legged authentication.
* @param {string[]} scopes List of resource access scopes.
* @returns {AuthClientTwoLegged} 2-legged authentication client.
*/
async function getClient(scopes) {
scopes = scopes || config.scopes.internal;
const key = scopes.join("+");
if (cache[key]) return cache[key];

try {
const { client_id, client_secret } = config.credentials;
let client = new AuthClientTwoLegged(
client_id,
client_secret,
scopes || config.scopes.internal,
true
);
let credentials = await client.authenticate();
cache[key] = client;
console.log(`OAuth2 client created for ${key}`);
return client;
} catch (ex) {
return null;
}
}

module.exports = {
getClient,
};

The project is ready! At this point your project should look like this