[Tutorial] Interacting with OpenAI in Node.js and Express
Published: 2023-02-13 18:06:00

---
title: [Tutorial] Interacting with OpenAI in Node.js and Express
published: true
description: An introduction to using Node.js and Express to interact with OpenAI's /completions endpoint.
tags: 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwsncitkg4woh01j7eyf.png
published_at: 2023-02-13 18:06 +0000
---

> ✨ This article is the first article and tutorial sent on the [https://frontendfresh.com](frontendfresh.com) newsletter.

Today's article is the first article of a 3 to 4-part tutorial series where we'll be working towards building our very own custom Q&A chatbot. We'll achieve this by:

- Building a Node/Express server to interact with OpenAI's APIs (today's email).
- Using React to build the UI of our Q&A chatbot.
- Finally, we'll then investigate how to fine-tune our app to have our Q&A chatbot return custom information.

Our final app will look something like the following:

![custom_gpt_2.gif](https://buttondown-attachments.s3.us-west-2.amazonaws.com/images/c08a3fd5-9364-4e45-b874-091c27fd23ab.gif) 

---

Today, we'll be focusing solely on creating a Node.js server where we can interact directly with OpenAI's APIs. This is a precursor to setting up our front-end app that would then interact with the local API we'll create.

If you want to follow along, you'll need [Node](https://nodejs.org/) and NPM installed in your machine and an OpenAI API key (we'll show you how to get this in the next section).

## Generating an OpenAI API key

Follow these steps to generate an API key with OpenAI:

- Sign up for an account by going to the OpenAI website ([https://platform.openai.com/](https://platform.openai.com/)).
- Once an account is created, visit the API keys page at [https://platform.openai.com/account/api-keys](https://platform.openai.com/).
- Create a new key by clicking the "Create new secret key" button.

When an API key is created, you'll be able to copy the secret key and use it when you begin development.

![image.png](https://buttondown-attachments.s3.us-west-2.amazonaws.com/images/fd5739c8-b8f9-4d94-9511-f8e25decde9d.png)

> Note: OpenAI currently provides 18$ free credits for 3 months which is great since you won't have to provide your payment details to begin interacting with the API for the first time.

## Setting up a Node/Express app

We'll now move towards creating a new directory for our Node project and we'll call it `custom_chat_gpt`.

```shell
mkdir custom_chat_gpt
```

We'll navigate into the new directory and we'll run the following command to create a `package.json` file.

```shell
npm init -y
```

Once the `package.json` file has been created appropriately, we'll then install the three dependencies we'll need for now.

```shell
npm install dotenv express openai
```

- `dotenv`: will allow us to load environment variables from a `.env` file when working locally.
- `express`: is the Node.js framework we'll use to spin up a Node server.
- `openai`: is the Node.js library for the OpenAI API.

We'll then create a file named `index.js`. The `index.js` file is where we'll build our Node.js/Express server.

In the `index.js` file, we'll:

- Include the `express` module with `require("express")`.
- We'll then run the `express()` function to define the Express instance and assign it to a constant labeled `app`.
- We'll specify a middleware in our Express instance (with `app.use()`) to parse incoming JSON requests and place the parsed data in a `req` body.
- We'll specify a `port` variable that will be given a value that comes from a `PORT` environment variable or `5000` if the `PORT` environment variable is undefined.

```js
const express = require("express");

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

const port = process.env.PORT || 5000;
```

We'll then set up a `POST` route labeled `/ask` that will act as the endpoint our client will trigger. In this route, we will expect a `prompt` value to exist in the request body and if it doesn't, we'll throw an error. If the `prompt` value does exist, we'll simply return a response of status `200` that contains the `prompt` in a `message` field.

Lastly, we'll run the `app.listen()` function to have our app listen on the port value we've specified in the `port` variable.

```javascript
const express = require("express");

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

const port = process.env.PORT || 5000;

// POST request endpoint
app.post("/ask", async (req, res) => {
  // getting prompt question from request
  const prompt = req.body.prompt;

  try {
    if (prompt == null) {
      throw new Error("Uh oh, no prompt was provided");
    }

    // return the result
    return res.status(200).json({
      success: true,
      message: prompt,
    });
  } catch (error) {
    console.log(error.message);
  }
});

app.listen(port, () => console.log(`Server is running on port ${port}!!`));
```

With this change saved, it will be a good time to test that our changes work. We'll run the server with:

```shell
node index.js
```

With our server running, we can then attempt to trigger our `/ask` POST request through a `CURL` command to verify our server is set up appropriately.

```js
curl -X POST \
  http://localhost:5000/ask \
  -H 'Content-Type: application/json' \
  -d '{ "prompt": "Hi! This is a test prompt!" }'
```

We'll be provided with a successful response and our prompt returned to us.

![image.png](https://buttondown-attachments.s3.us-west-2.amazonaws.com/images/70203445-094d-4f8b-9c43-ff5a1950cc31.png) 

With our server now working as intended, we can move towards having our `/ask` endpoint interact with OpenAI's `/completions` endpoint.

## Interacting with OpenAI's completions endpoint

OpenAI provides a [`/completions` endpoint](https://platform.openai.com/docs/api-reference/completions/create) in their API that provides completion suggestions for text input. 

When we send a request to the `/completions` endpoint and we include a prompt or seed text, the API will generate a continuation of that text based on its training data.

With this `/completions` endpoint, we can build our own custom version of [ChatGPT](https://chat.openai.com/) (with some caveat that ChatGPT most likely uses a more powerful Machine Learning model that isn't available through the OpenAI API).

To interact with the OpenAI API, we will need the unique API key that we created earlier through the OpenAI website. Sensitive information, such as API keys, should not be hard-coded directly into application source code.

We'll create a `.env` file in the root directory of our project to store environment variables that contain sensitive information.

```shell
custom_chat_gpt
  .env
  // ...
```

In the `.env` file, we'll create a new environment variable labeled `OPENAI_API_KEY` and give it the value of the OpenAI API key.

```txt
# your unique API key value goes here
OPENAI_API_KEY=sk-############
```

In our `index.js` file, we'll require and use the `dotenv` module to load environment variables from the `.env` file into the `process` environment of our application. We'll also import the classes we'll need from the `openai` Node.js library — the `Configuration` and `OpenAIApi` classes.

```js
// configure dotenv
require("dotenv").config();

// import modules from OpenAI library
const { Configuration, OpenAIApi } = require("openai");

// ...
```

Next, we'll need to create a configuration object for interacting with the OpenAI API. We'll do this by instantiating the `Configuration()` constructor and passing in the value of the `OPENAI_API_KEY` environment variable to the `apiKey` field.

```js
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
```

We'll then set up a new instance of the OpenAI API class like the following:

```js
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
```

We can now use the `openai` variable we've created to make API calls and process responses from OpenAI.

In our `/ask` POST request function, we'll run the `openai.createCompletion()` function that essentially triggers a call to OpenAI's completions endpoint.

```js
app.post("/ask", async (req, res) => {
  const prompt = req.body.prompt;

  try {
    if (prompt == null) {
      throw new Error("Uh oh, no prompt was provided");
    }

    // trigger OpenAI completion
    const response = await openai.createCompletion();

    // ...
  } catch (error) {
    console.log(error.message);
  }
});
```

The OpenAI completions endpoint allows us to pass in a [large number of optional fields](https://platform.openai.com/docs/api-reference/completions/create) into the request to modify how we want our text completion to behave. For our use case, we'll only look into providing values for two fields — `model` and `prompt`.

- `model`: specifies the name of the language model that the API should use to generate the response to the request. OpenAI provides several different language models, each with its strengths and capabilities. For our use case, we'll specify that we want to use the `text-davinci-003` model which is OpenAI's [most capable GPT-3 model](https://platform.openai.com/docs/models/gpt-3).
- `prompt`: is the prompt that we want OpenAI to generate a completion for. Here we'll just pass the `prompt` value that exists in the body of our `/ask` request.

```js
app.post("/ask", async (req, res) => {
  const prompt = req.body.prompt;

  try {
    if (prompt == null) {
      throw new Error("Uh oh, no prompt was provided");
    }

    // trigger OpenAI completion
    const response = await openai.createCompletion({
      model: "text-davinci-003",
      prompt,
    });

    // ...
  } catch (error) {
    console.log(error.message);
  }
});
```

The text returned from the OpenAI response exists within a `choices` array that itself is within a `response.data` object. We'll attempt to access the text returned from the first choice returned in the API which will look like the following:

```js
app.post("/ask", async (req, res) => {
  const prompt = req.body.prompt;

  try {
    if (prompt == null) {
      throw new Error("Uh oh, no prompt was provided");
    }

    const response = await openai.createCompletion({
      model: "text-davinci-003",
      prompt,
    });
    // retrieve the completion text from response
    const completion = response.data.choices[0].text;

    // ...
  } catch (error) {
    console.log(error.message);
  }
});
``` 

The last thing we'll do is have this completion response be returned in the successful response of our `/ask` request. With this change and all the changes we've made, our `index.js` file will look like the following.

```js
require("dotenv").config();
const express = require("express");
const { Configuration, OpenAIApi } = require("openai");

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

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

const port = process.env.PORT || 5000;

app.post("/ask", async (req, res) => {
  const prompt = req.body.prompt;

  try {
    if (prompt == null) {
      throw new Error("Uh oh, no prompt was provided");
    }

    const response = await openai.createCompletion({
      model: "text-davinci-003",
      prompt,
    });
    const completion = response.data.choices[0].text;

    return res.status(200).json({
      success: true,
      message: completion,
    });
  } catch (error) {
    console.log(error.message);
  }
});

app.listen(port, () => console.log(`Server is running on port ${port}!!`));
```

We'll save our changes and restart our server. With our server running, we can ask certain questions to our API like `"What is the typical weather in Dubai?"`.

```js
curl -X POST \
  http://localhost:5000/ask \
  -H 'Content-Type: application/json' \
  -d '{ "prompt": "What is the typical weather in Dubai?" }'
```

After waiting a few seconds, our API will return to us a valid answer to our question!

![image.png](https://buttondown-attachments.s3.us-west-2.amazonaws.com/images/4e3072e2-e250-4a6e-91f5-8a07d906d25c.png)

That's it! We've managed to build a simple API with Node/Express to interact with OpenAI's completions endpoint.

Next week, we'll be continuing on this tutorial by building a React app that triggers the `/ask` request when an input field is submitted.

## Closing thoughts

- You can find the source code for this article at [github.com/djirdehh/frontend-fresh/articles_source_code](https://github.com/djirdehh/frontend-fresh/tree/main/articles_source_code/custom_chat_gpt).
- Do interact with the `/completions` endpoint and feel free to ask it any prompt that you would like!
- If you're not a fan of using `curl` to test requests locally, [Postman](https://www.postman.com/) is a popular tool to test API requests through a client.
- Do read more on all the different optional fields we can provide in OpenAI's `/completion` endpoint in the [OpenAI API documentation](https://platform.openai.com/docs/api-reference/completions/create).
- Subscribe to [https://www.frontendfresh.com/](https://www.frontendfresh.com/) for more tutorials like this to hit your inbox on a weekly basis!

🙂

— Hassan ([@djirdehh](https://twitter.com/djirdehh))