Unleashing the Power of GPT-3 with a Serverless Node.js REST API: A Step-by-Step Guide
Tags: gpt3, serverless, node, database
Published: 2023-01-22 09:39:09

This is an example of a serverless Node.js application that uses codehooks.io create a REST API for the OpenAI GPT-3 language model (which similar in many ways to [ChatGPT](https://chat.openai.com)). The API allows REST API clients to send text prompts to the GPT-3 model and receive a response in return. 

The code uses the codehooks-js library to handle routing and middleware for the API, and the node-fetch library to make HTTP requests to the OpenAI API. The code also uses the built-in codehooks.io Key-Value store to cache the response from the OpenAI API for 60 seconds, in order to reduce the number of requests made to the OpenAI API (If you want to create your own Node.js Express backend, you just need to replace this cache with [Redis](https://redis.io/) for example).

Additionally, the code also implements a middleware to rate limit the number of requests coming from the same IP address to 10/min.

> This article is also available in the [official Codehooks.io docs](https://codehooks.io/docs/examples/chat-gpt-rest-api-nodejs-example).

**Documentation and suggested reading**
* Read more about the [OpenAI API](https://beta.openai.com/docs/introduction)
* Read more about the [Key-Value store ](https://codehooks.io/docs/nodekeyval.md)
* Blog article on [creating code with chatgpt](https://codehooks.io/blog/how-to-create-a-nodejs-restapi-backend-using-chatgpt)


## Create a new OpenAPI API key

Navigate to your [Open AI account settings](https://beta.openai.com/account/api-keys) and create a new secret key.

![chatgpt-apikey](https://codehooks.io/assets/images/chatgpt-apikey-835acbcf50da546cb8e3b90b72985a03.png)

## Create a new codehooks.io project to host the GPT-3 REST API

```bash
coho create chatgptrestapi
```
This will create a new directory for your REST API backend application.

```bash
cd chatgptrestapi
```

Open the `index.js` file and replace the code with the source code at the end of this example.

## Create a new secret environment variable for the serverless backend app

Once you have created your Open AI API secret key, you need to add this to the serverless runtime. You can use the [CLI command](https://codehooks.io/docs/cli.md#set-env) for this.

```bash
coho set-env OPENAI_API_KEY 'XXXXXXXX' --encrypted
```


## Deploy the Node.js ChatGPT-like REST API to the codehooks.io backend 
From the project directory run the [CLI command](https://codehooks.io/docs/cli#deploy) `coho deploy`.
The output from my test project is shown below:

```bash
coho deploy 
Project: chatgptrestapi-pwnj  Space: dev

Deployed Codehook successfully! 🙌 
```

## Test your ChatGPT REST API endpoint

The following curl example demonstrates that we have a live REST API that takes a `POST` body with a text string `ask` and returns the response from the Open AI API.

```bash
curl -X POST \
  'https://chatgptrestapi-pwnj.api.codehooks.io/dev/chat' \
  --header 'x-apikey: cb17c77f-2821-489f-a6b4-fb0dae34251b' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "ask": "Tell a joke"                  
}'
```

Output from the GPT-3 REST API is shown below 😂:

```bash
Q: What did the fish say when it hit the wall?
A: Dam!                                 
```

## Full source code example

```js
/*
* GPT-3 REST API example using serverless node.js and codehooks.io
*/

import { app, Datastore } from 'codehooks-js';
import fetch from 'node-fetch';


// REST API routes
app.post('/chat', async (req, res) => {
    if (!process.env.OPENAI_API_KEY) return res.status(500).end('Please add your OPENAI_API_KEY'); // CLI command: coho set-env OPENAI_API_KEY 'XXX' 
    const { ask } = req.body;
    const db = await Datastore.open();
    const cacheKey = 'chatgpt_cache_' + ask;

    // check cache first    
    const cachedAnswer = await db.get(cacheKey);

    // get from cache or OpenAi
    if (cachedAnswer) {
        res.end(cachedAnswer)
    } else { // get from Open AI API

        // pick text element from the OpenAI response by JS nested destructuring
        const { choices: { 0: { text } } } = await callOpenAiApi(ask);
        console.log(ask, text);

        // add to cache for 1 minute
        await db.set(cacheKey, text, { ttl: 60 * 1000 });
        // send text back to client
        res.end(text);
    }
})

// Call OpenAI API
async function callOpenAiApi(ask) {
    const raw = JSON.stringify({
        "model": "text-davinci-003",
        "prompt": ask,
        "temperature": 0.6,
        "max_tokens": 1024,
        "stream": false
    });

    var requestOptions = {
        method: 'POST',
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${process.env.OPENAI_API_KEY}`
        },
        body: raw,
        redirect: 'follow'
    };

    const response = await fetch("https://api.openai.com/v1/completions", requestOptions);
    return response.json();

}

// global middleware to IP rate limit traffic
app.use(async (req, res, next) => {
    const db = await Datastore.open();
    // get client IP address
    const ipAddress = req.headers['x-real-ip'];
    // increase count for IP
    const count = await db.incr('IP_count_' + ipAddress, 1, { ttl: 60 * 1000 })
    console.log(ipAddress, count);
    if (count > 10) {
        // too many calls
        res.status(429).end("Sorry too many requests for this IP");
    } else {
        // ok to proceed
        next();
    }
})

// Export app to the serverless runtime
export default app.init();
```

Full source is available on our [GitHub example repo](https://github.com/RestDB/codehooks-io-examples/tree/main/chatgpt-rest-api). We will update this example when ChatGPT becomes available as an API.