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