Building a language learning tool with Supabase Edge functions + GPT3.5
Tags: supabase, webdev, javascript, tutorial
Published: 2023-04-25 13:25:21

I’m learning Japanese and I love News Web EASY from NHK - it features one news story every day written in Japanese that a beginner can understand.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a28c1h6tatuy8a8mue5s.png)

So when I saw [Supabase](https://supabase.com/) launched self hosted edge functions, I wanted to try building a simple version that translates and simplifies football news articles.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dis90xl59rk2ajokyoio.png)
[Easy Japanese - Football News](https://easy-japanese-news-football.tiiny.site/)

Here's the code https://github.com/jackbridger/japanese-simple-football-news

How it works:
1. Grabs a football article written in English (now this is hard-coded but in future I'll use an API or scraper)
2. Checks if the article has already been processed by checking the cache (Redis)
3. If so, return the article to the client
4. If not, translate the article 
5. Then convert the translation into simple Japanese using GPT 3.5 
6. Return to the client

## Replicating it 
For setup, we’re going to roughly follow [this article](https://supabase.com/blog/edge-runtime-self-hosted-deno-functions) from the Supabase team. 

I added this into serve for main/index.ts and pretty much removed everything else.

I also recommend [consulting the final code](https://github.com/jackbridger/japanese-simple-football-news) as you go through as I'm not covering everything I changed 

```js
    const supabaseClient = createClient(
      Deno.env.get('SUPABASE_API_URL'),
      Deno.env.get('SUPABASE_API_ANON_KEY'),
      { global: { headers: { Authorization: req.headers.get('Authorization')! } } }
    )
```

Be sure to set 

```flyctl secrets set SUPABASE_API_ANON_KEY=```
and
```flyctl secrets set SUPABASE_API_URL=```

**Sign up for an [Fly.io](http://fly.io/) account and install [flyctl](https://fly.io/docs/hands-on/install-flyctl/)**

```bash
brew install flyctl
```

Clone the [demo repository](https://github.com/supabase/self-hosted-edge-functions-demo) to your machine

```bash
git clone https://github.com/supabase/self-hosted-edge-functions-demo.git && cd self-hosted-edge-functions-demo
```

**Install deno**

We need to **[install deno](https://deno.land/manual@v1.32.4/getting_started/installation)** to run our functions locally

```bash
brew install deno
```

Then we can log into fly 

```bash
flyctl auth login
```

And then we can run 

```bash
fly launch
```

Here are the options I selected (make sure to select yes on redis/upstash)

```
➜  self-hosted-edge-functions-demo git:(main) ✗ fly launch
Creating app in /Users/Jack/programming/supa/self-hosted-edge-functions-demo
An existing fly.toml file was found for app supa-edge-demo
? Would you like to copy its configuration to the new app? Yes
Scanning source code
Detected a Dockerfile app
? Choose an app name (leaving blank will default to 'supa-edge-demo') easy-japanese-football
automatically selected personal organization: Jack Bridger
App will use 'syd' region as primary
Created app 'easy-japanese-football' in organization 'personal'
Admin URL: https://fly.io/apps/easy-japanese-football
Hostname: easy-japanese-football.fly.dev
? Would you like to set up a Postgresql database now? No
? Would you like to set up an Upstash Redis database now? Yes
? Select an Upstash Redis plan Free: 100 MB Max Data Size
```


We need to add our supabase crednetials too:

Then add make the call to the deepL api - you’ll need to sign up for a free account and get an api key (it’s free but you need a credit card)

Set your DeepL API key 
```flyctl secrets set DEEPL_API_KEY=*YOUR_API_KEY*```

```js
async function translateTextToJapanese(text: string): Promise<string> {
  const response = await fetch(DEEPL_API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Authorization": `DeepL-Auth-Key ${Deno.env.get('DEEPL_API_KEY')}`,
    },
    body: new URLSearchParams({
      text: text,
      target_lang: "JA",
    }),
  });
```


Then make the call to openai, It’s not free but it should be cents per api call and I think there is a free trial.

Set your API key 
```flyctl secrets set OPENAI_API_KEY=*API_KEY*```

```js
    async function rewriteToN5Japanese(text) {
      try {
const openAI = new OpenAI(Deno.env.get('OPENAI_API_KEY'));
        const response = await openAI.createChatCompletion({
          model: "gpt-3.5-turbo",
          messages: [
            {
              role: "system",
              content: "わかりやすい日本語を書くのが得意な親切なアシスタントさんですね。特にn-5レベルの外国人向けの文章を得意としています。", // You are a helpful assistant that is great at writing easy-to-understand Japanese. You are especially good at writing for n-5 level foreigners.
            },
            {
              role: "user",
              content: `以下の文章を、N5レベルの簡単な文章に書き換える。: ${text}`, // Rewrite the following text into simple N5 level text
            },
          ],
          max_tokens: 200, // Limit the response length
        });
        

        const translatedText = response.choices[0].message.content.trim();
        return translatedText;
      } catch (error) {
        console.error('Error translating text:', error);
        return '';
      }
    }
```


Then we need to get our redis crednetials from upstassh and 

We import redis and initialise it like this.

```js
import {
  connect as redisConnect
} from 'https://deno.land/x/redis/mod.ts';
...
      const redisClient = await redisConnect({
        hostname: "fly-easy-japanese-football-redis.upstash.io",
        port: 6379,
        password: Deno.env.get('REDIS_PW'),
        maxRetryCount: 10,
        retryInterval: 100000,
      });      
```

Then to check the cache we use this code:

```js
      if (translatedAlr){
        console.log("already exists")
        translatedAndSimplified = translatedAlr
        return new Response(JSON.stringify(translatedAndSimplified), {
          headers,
          status: 200,
        })
      }
```

Notice how redis is really just a key value look up. 

Now we can do fly deploy

```fly deploy```

And then we can check it out by going to the dashboard at fly.io

Then, I went to chat.opneai.com and used gpt 4 to generate some basic html using this prompt:

```
Make a html page for me that queries this api https://jp-football.fly.dev/

"リバプールは、サラー選手とイオタ選手が得点し、リーズ・ユナイテッドを2-1で破りました。これで、リバプールはプレミアリーグで5試合ぶりの勝利となりました。前の試合では、5失点しましたが、今回は攻撃面で優位に立ちました。"

It gets back an article like the above.

It should be a single page with a random unsplash football image.

The text should be nice to read, in the style of Medium (the blogging site).

The title is "Easy Japanese Football News"

Use only html css and js
```

I copy paste this into an index.html file And it looks pretty decent.

### Deploying the html page

Finally. I deployed the one page site on [Tiiny](https://tiiny.host/) - which was simply a case of dragging my index.html

https://easy-japanese-news-football.tiiny.site/

The next step is to set this up to work with a news api or do some scraping. And to refine my gpt prompt as the Japanese translation isn't perfect yet.

Here's the full code again https://github.com/jackbridger/japanese-simple-football-news

If you like this article, I also host a podcast on [developer tools](https://scalingdevtools.com/).