Tutorials

How to Make a ChatGPT Plugin With TypeScript and Express.js

Nick Franck
#openai#chatgpt#typescript#express#chatgpt plugins#openapi

ChatGPT plugins provide developers with the opportunity to utilize OpenAI’s powerful GPT-4 model. Plugins give ChatGPT access to tools via a REST API. When a plugin is installed in a user’s ChatGPT session, the model may decide to use the plugin as part of its response.

You can develop plugins that allow ChatGPT to plan travel, go shopping, or compute complex math. In this tutorial, you’ll learn the basics of developing a ChatGPT plugin using TypeScript and Express.js.

This tutorial assumes you have Node.js and TypeScript installed. For information about creating a plugin in other languages, see:

Step 1: Set up the TypeScript project

Create a new directory for your project:

mkdir typescript-chatgpt-plugin
cd typescript-chatgpt-plugin

Initialize a new Node.js project and install the required packages:

npm init -y
npm install express cors swagger-jsdoc swagger-ui-express
npm install --save-dev typescript ts-node nodemon @types/node @types/express @types/cors @types/swagger-jsdoc @types/swagger-ui-express

Next, create the tsconfig.json file to configure your TypeScript project:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "es2017",
    "noImplicitAny": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "out",
    "baseUrl": ".",
    "paths": {},
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true
  },
  "include": ["src/**/*"],
  "exclude": ["**/*.spec.ts", "out"]
}

Now, create the src directory and a src/index.ts file as the starting point for your application:

import express, { Request, Response } from "express";

const app = express();
app.use(express.json());

const port = 8000;
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

Finally, add scripts to package.json for starting and running the development server:

"scripts": {
  "start": "ts-node src/index.ts",
  "start:dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts"
}

Step 2: Create the plugin API

ChatGPT interacts with plugins via a defined REST API. You can quickly create a REST API in TypeScript using Express.js and document it with OpenAPI using swagger-jsdoc. ChatGPT uses the OpenAPI schema to understand how to interact with your plugin, so it’s important to document your API correctly. First, update the src/index.ts file:

import express, { Request, Response } from "express";
// <---------- NEW CODE START ---------- >
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";

const options = {
  definition: {
    openapi: "3.0.0",
    info: {
      title: "ChatGPT TypeScript Plugin",
      version: "1.0.0",
    },
    components: {
      schemas: {
        HelloResponse: {
          type: 'object',
          properties: {
            message: {
              type: 'string',
            },
          },
        },
      },
    },
  },
  apis: ["src/index.ts"],
};

const specs = swaggerJsdoc(options);
// <---------- NEW CODE END ---------- />

const app = express();
app.use(express.json());

// <---------- NEW CODE START ---------- >
/**
 * @swagger
 * /hello:
 *  get:
 *    summary: Returns a hello message from the plugin
 *    operationId: getHello
 *    responses:
 *      200:
 *        description: A successful response
 *        content:
 *          application/json:
 *            schema:
 *              $ref: '#/components/schemas/HelloResponse'
 */
app.get("/hello", (req: Request, res: Response) => {
  res.json({ message: "Hello from the plugin!" });
});

app.use("/api", swaggerUi.serve, swaggerUi.setup(specs));

app.get("/api-json", (req, res) => {
  res.setHeader("Content-Type", "application/json");
  res.send(specs);
});
// <---------- NEW CODE END ---------- />

const port = 8000;
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

Now, a GET route is available at the path /hello which returns a JSON object with the given message from the plugin.

Step 3: Set up CORS middleware for development

In order to test your plugin locally before deploying to production, you need to configure your application to allow Cross-Origin Resource Sharing (CORS) from the ChatGPT website. CORS enables ChatGPT to request access to resources from your locally running webserver. To set up CORS with Express.js, modify the src/index.ts file:

import express, { Request, Response } from "express";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
// <---------- NEW CODE START ---------- >
import cors from "cors";
// <---------- NEW CODE END ---------- />

// ...

const app = express();
app.use(express.json());
// <---------- NEW CODE START ---------- >
app.use(
  cors({
    origin: "https://chat.openai.com",
  })
);
// <---------- NEW CODE END ---------- />

// ...

This will configure CORS for your application from https://chat.openai.com.

Step 4: Create the plugin manifest

Every ChatGPT plugin must include a plugin manifest hosted on the same domain as the API. ChatGPT looks for the plugin specifically at the path /.well-known/ai-plugin.json.

Create a new file called ai-plugin.json in your project directory, and add the following:

{
  "schema_version": "v1",
  "name_for_human": "My First plugin",
  "name_for_model": "helloPlugin",
  "description_for_human": "My first ChatGPT plugin",
  "description_for_model": "Plugin which says hello when the user asks you to say hello",
  "auth": {
    "type": "none"
  },
  "api": {
    "type": "openapi",
    "url": "http://localhost:8000/api-json",
    "is_user_authenticated": false
  },
  "logo_url": "http://localhost:8000/logo.png",
  "contact_email": "support@example.com",
  "legal_info_url": "http://www.example.com/legal"
}

In this example, the “auth” field is set to “none” because this plugin doesn’t require authentication. However, depending on your use case, you might need your plugin to handle authentication. For more information about implementing authentication in a ChatGPT plugin, refer to our ChatGPT Plugin Authentication Guide.

Next, set up a route for accessing the plugin manifest from your app in src/index.ts:

import express, { Request, Response } from "express";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
import cors from "cors";
// <---------- NEW CODE START ---------->
import * as fs from "fs";
import * as util from "util";
// <---------- NEW CODE END ---------- />

// ...

app.get("/api-json", (req, res) => {
  res.setHeader("Content-Type", "application/json");
  res.send(specs);
});

// <---------- NEW CODE START ---------->
app.get("/.well-known/ai-plugin.json", async (req: Request, res: Response) => {
  const readFile = util.promisify(fs.readFile);
  const data = await readFile("ai-plugin.json", "utf8");
  res.json(JSON.parse(data));
});
// <---------- NEW CODE END ---------- />

const port = 8000;
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

Step 5: Install and test your plugin

First, start your plugin server locally by running:

npm run start

You can verify it worked by navigating to http://localhost:8000/api in your browser:

Swagger OpenAPI Typescript plugin page

Next, go to the ChatGPT UI, select the plugin model, and choose “Develop your own plugin” from the plugins dropdown menu.

Selecting develop your own plugin

Finally, type your localhost URL into the plugin form:

Typing the address of your plugin

After a while, you’ll see the following:

Successfully adding your plugin

Click “Install localhost plugin” to proceed. With your plugin enabled, you can start to interact with it using ChatGPT. For example, try typing in “Say hello from my plugin” into the chat window:

Successfully using the plugin

Congratulations! You’ve successfully created your first ChatGPT plugin with TypeScript and confirmed it works locally. Next, check out our deployment and hosting guide to learn about deploying your plugin to production.

Enjoyed this post?
Subscribe for more!

Get updates on new content, exclusive offers, and exclusive materials by subscribing to our newsletter.

← Back to Blog