Integrating OpenAI's GPT in a React Native App

11 May
  • tutorial, programming, technical

Let’s start with the basics of how to use OpenAI’s GPT language models programmatically from NodeJS.

After setting up a new NodeJS project folder, we should install the official API handler library:

npm install openai

…or, if you prefer to use yarn instead of npm:

yarn add openai

In this project folder create a file called gpt-cli.mjs:

// gpt-cli.mjs

import { Configuration, OpenAIApi } from "openai";

const OPENAI_API_KEY = 'YOUR_API_KEY'; // replace this with your actual API key

const config = new Configuration({
apiKey: OPENAI_API_KEY,
});

const openai = new OpenAIApi(config);

const prompt = process.argv[2] || 'Hello world';

async function main() {
    try {
        const result = await openai.createChatCompletion({
            messages: [
                {
                role: 'system',
                content: prompt,
                },
            ],
            model: 'gpt-3.5-turbo',
        });
        console.log(result.data.choices[0].message.content);
    }
    catch (e) {
        console.error(e.response?.data?.error?.message || e);
    }
}

main();

Now try running this from the project folder:

node gpt-cli.mjs "Write a haiku about a cat."

If all goes well, after a brief pause, a cat-themed haiku should be displayed in the terminal in response.

Mine was this:

Graceful feline moves, Purring softly, eyes a-glow, Cat’s presence, calmness.

If you see an error message instead, double check that your API Key is valid.

Now we have an example of using the GPT API from a simple command-line interface, we can move on to a slightly more complex goal: we want to create a server with our own simple API that will act as a wrapper for the GPT API server. The reason we do need to do it this way is that the API Key is a secret that we should not embed in client-side code, and that includes Android/iOS apps.

Create a new file called gpt-server.mjs:

// gpt-server.mjs

import { Configuration, OpenAIApi } from "openai";
import { createServer } from "node:http";

const OPENAI_API_KEY = 'YOUR_API_KEY'; // replace this with your actual API key
const SERVER_PORT = 8184;

const config = new Configuration({
  apiKey: OPENAI_API_KEY,
});

const openai = new OpenAIApi(config);

const server = createServer(async (req, res) => {
  switch (req.url) {
    case '/api': {
      res.setHeader('Content-Type', 'application/json');
      if ((req.headers['content-type'] || '').replace(/\s*;.*$/, '') !== 'application/json') {
        res.writeHead(400);
        res.end(JSON.stringify({error:'invalid json'}));
        break;
      }
      const buffers = [];

      for await (const chunk of req) {
        buffers.push(chunk);
      }

      let json;
      try {
        json = JSON.parse(Buffer.concat(buffers).toString());
      }
      catch {
        res.writeHead(400);
        res.end(JSON.stringify({error:'invalid json'}));
        break;
      }
      const result = await openai.createChatCompletion({
        messages: [
          {
            role: 'system',
            content: json.prompt,
          },
        ],
        model: 'gpt-3.5-turbo',
      });
      res.writeHead(200);
      res.end(JSON.stringify({response:result.data.choices[0].message.content}));
      break;
    }
    default: {
      res.setHeader('Content-Type', 'text/html;charset=utf-8');
      res.writeHead(404);
      res.end('

404 Not Found

'); break; } } }); server.listen(SERVER_PORT, () => { console.log(`Server is running on port ${SERVER_PORT}. Hit Ctrl-C to end.`); });

With this script running, you will be able to make HTTP POST requests to /api port 8184 with JSON payload in the form {prompt:'...'} and should get back a JSON response in the form {response:'...'}.

At this point, we need to know the IP address of the computer running this script on the local wi-fi network, so the device running our React Native app can connect to it. You can use this NodeJS snippet to list the local network connections:

let ifaces = require('os').networkInterfaces(); for (let [name, nets] of Object.entries(ifaces)) for (const net of nets) if (!net.internal && [4,'IPv4'].includes(net.family)) console.log(name, net.address)

If you install the tiny-json-http library, you can use this snippet (be sure to replace YOUR_IP_HERE) to test our API wrapper service:

require('tiny-json-http').post({url:'http://YOUR_IP_HERE:8184/api', data: {prompt:'Hello world'}}).then(resp => console.log(resp.body.response)).catch(e => console.error(e))

You may need to configure your firewall to make sure our server is accessible through the local network IP.

Assuming that is all successful, let’s start our React Native app. First, follow the official recommendations for setting up a React Native project. Once you have it set up, edit the App.tsx file. Remove what is there currently and add this:

// App.tsx

import React from 'react';
import { ActivityIndicator, Alert, Button, ScrollView, Text, TextInput, View } from 'react-native';

const API_HOST = 'YOUR_IP_HERE';
const API_PORT = 8184;

const App = () => {
  const [submitting, setSubmitting] = React.useState(false);
  const [storyTopic, setStoryTopic] = React.useState('A teddy bear');
  const [storyText, setStoryText] = React.useState('');
  React.useEffect(() => {
    if (submitting) {
      let stillHere = true;
      fetch(`http://${API_HOST}:${API_PORT}/api`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({prompt: `Write a short story on the following topic: ${storyTopic}`}),
      })
      .then((res) => {
        return res.json();
      })
      .then((json) => {
        if (stillHere) {
          if (typeof json.response === 'string') {
            setStoryText(json.response);
          }
          else if (typeof json.error === 'string') {
            Alert.alert('Error', json.error);
          }
          else {
            Alert.alert('Error', 'Invalid response');
          }
        }
      })
      .catch(e => {
        Alert.alert('Error', 'An internal error occurred');
        console.log(e);
      })
      .finally(() => {
        if (stillHere) setSubmitting(false);
      });
      return () => { stillHere = false; };
    }
  }, [submitting]);
  return (
    <View style=>
      <View style=>
        Enter the topic for your story here:
        <TextInput value={storyTopic} onChangeText={setStoryTopic} editable={!submitting} style=/>
      </View>
      <View style=>
        <Button title="Submit" onPress={() => setSubmitting(true)} disabled={submitting} />
      </View>
      {
        submitting &&
        
      }
      <ScrollView style=>
        {storyText}
      </ScrollView>
    </View>
  )
};

export default App;

Now run npm run android (or yarn android if you are using yarn). Once the React Native app has finished building and launches on your device, you should see an editable text field to specify the subject for a short story and a button. Pressing the button will submit the story topic through our wrapper API to OpenAI’s GPT API, and display the result once it returns.