Build a LinkedIn Bio Generator in Laravel using the REST API of OpenAI, Livewire and Tailwind CSS.
Tags: openai, laravel, tailwindcss, livewire
Published: 2023-05-21 10:48:43

[Laravel](https://laravel.com/) is a PHP framework with expressive, elegant syntax that strives to provide an amazing developer experience while providing powerful features.
In this article, we will build a LinkedIn Bio Generator in Laravel using the REST API of OpenAI.  The Tailwind CSS and Livewire part will be for a nice graphical user interface. ✨
At the end, you’ll have a web page where the user will provide one or more keywords that will be sent to OpenAI's API.  The API will generate a bio containing those keywords and it will be shown to the user. This web page will look like this : 


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

So let's dive in ! 🚀🚀

## Prerequisites

We will create our project using the latest version of Laravel (v10.3 by the time of writing this article). And for this, you should ensure that your local machine has PHP 8.1 or above and [Composer](https://getcomposer.org/) installed. In addition, [Node and NPM](https://nodejs.org/) should be installed too.

## Project Setup

Let’s create a new project with the following command :

```
composer create-project laravel/laravel linkedin-bio-generator
```

Once the project has been created, move to its root directory and run the below command to install the livewire package : 

```
composer require livewire/livewire
```

Livewire is a full-stack framework that makes building dynamic interfaces simple, without leaving the comfort of Laravel. It simplifies the complexity that Vue or React brings up. 🤩

Next, you can install your application's frontend dependencies via NPM : 

```
npm install
```

Also, let’s use the [OpenAI PHP library](https://github.com/openai-php/client) to work with the REST API of OpenAI. This library is a supercharged community-maintained PHP API client that allows you to interact with OpenAI API. We have to install it via composer :

```
composer require openai-php/client
```

You need to have an API key from [OpenAI platform](https://platform.openai.com/) to be able to communicate with their API. If you don’t already have an API key follow [this short tutorial](https://help.socialintents.com/article/188-how-to-find-your-openai-api-key-for-chatgpt) to create an account and get it.

Finally, we need to work with Tailwind CSS in our project for a good looking user interface. 😉  Follow [this link](https://tailwindcss.com/docs/guides/laravel) to know every steps you need for setting up.

Great ! We can now start building our [LinkedIn Bio Generator](https://github.com/nxvv/linkedin-bio-generator).  

## Create the user interface

We just need a single input to capture the keywords the AI should include into the LinkedIn Bio and a place where we can show the result. 
So, inside the directory `resources/views` let’s create a new directory named `layouts` which will contain a file named `app.blade.php`.

Here’s the content for the `resources/views/layouts/app.blade.php` file.

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>LinkedIn Bio Generator</title>

    @vite('resources/css/app.css')

    @livewireStyles

</head>
<body>
    
    <nav class="bg-gray-600 px-2 sm:px-4 py-2.5">
        <div class="container flex flex-wrap items-center justify-between mx-auto">
            <a href="/" class="flex items-center text-white text-2xl">
                LinkedIn Bio Generator
        </a>
        </div>
    </nav>

    <div class="mx-auto max-w-2xl">

        @livewire('show-form')

    </div>
  
    @livewireScripts

</body>
</html>
```

In the code above, we are adding two blade directives (`@livewireStyles` and `@livewireScripts`) to include the Livewire styles and scripts on the page. 
We are also rendering a Livewire component using this instruction: `@livewire('show-form')`. 
Don't worry, we will create this component right after 😉.
Finally, we are including in the `<head>`  Tailwind’s utility classes to style our content with this instruction : `@vite('resources/css/app.css')`

We can now run the following command to generate our Livewire component called `showForm` : 

```
php artisan make:livewire showForm
```

Running this command will generate the following two files : 

- `app/Http/Livewire/ShowForm.php`
- `resources/views/livewire/show-form.blade.php`

Let’s update the `resources/views/livewire/show-form.blade.php` file by the following content :

```
<div class="mt-8">
    <h1 class="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
        LinkedIn Bio Generator
    </h1>
    <p class="mt-6 text-base text-zinc-60">
        Save time. &#9203;
        Generate a LinkedIn bio by Artificial Intelligence ! &#128523;
    </p>
    <form class="mt-12" wire:submit.prevent="getBio">
        <div class="sm:flex sm:w-full sm:max-w-lg">
            <div class="min-w-0 flex-1">
                <div>
                    <div class="relative rounded-md shadow-sm">
                        <input type="text" wire:model.defer="keywords" name="keywords" id="keywords" 
                            class="block w-full rounded-md border 
                            border-gray-300 px-5 py-3 text-base  
                            text-gray-900 placeholder-gray-700 
                            shadow-sm focus:border-teal-500 
                            focus:ring-teal-500 " 
                            placeholder="Type keywords (PHP, Developer, ...)"
                        >
                    </div>
                    <p class="mt-2 text-sm text-red-600" id="keywords-error"></p>
                </div>
            </div>
            <div class="mt-4 sm:mt-0 sm:ml-3">
                <button type="submit" class="block w-full rounded-md border border-transparent
                    bg-gray-600 px-5 py-3 text-base font-medium text-white shadow 
                    hover:bg-emerald-500 focus:outline-none focus:ring-2 focus:ring-teal-500 
                    focus:ring-offset-2 sm:px-10"
                >
                    Get my biooo &#127870; &#127870;
                </button>
            </div>
        </div>
    </form>

    <div>
        @if ($bio)
            <h2 class="mt-4 mb-1 text-2xl font-medium">Your awesome bio!</h2>
            <div class="mb-12 bg-gray-100 text-grey-900 p-4 rounded-lg">
                {{$bio}}
            </div>
        @endif

        <div wire:loading.flex class="hidden p-6 justify-center">
            <div role="status">
                <svg class="inline mr-2 w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-gray-600 dark:fill-gray-300" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"></path>
                    <path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"></path>
                </svg>
                <span class="sr-only">Loading...</span>
            </div>
        </div>
    </div>
</div>
```

In the above code, we just created a single input to capture the keywords the AI should include into the LinkedIn Bio and a place where we can show the result. 

Finally, add a `resources/views/home.blade.php` file with this content : `@extends('layouts.app')`

## Use the AI power

Open the `app/Http/Livewire/ShowForm.php` file and update it with the following code : 

```
<?php

namespace App\Http\Livewire;

use Livewire\Component;

use OpenAI;

class ShowForm extends Component
{
    public $bio;
    public $keywords;

    public function render()
    {
        return view('livewire.show-form');
    }

    public function getBio()
    {
        $key = config('services.open_ai.api_key');
        $client = OpenAI::client($key);
        
        $result = $client->completions()->create([
            'model' => 'text-davinci-003',
           'prompt' => 'Write a LinkedIn bio including the keywords: ' . $this->keywords,
            'max_tokens' => 256,
            'temperature' => 1 
        ]);

        $this->bio = $result->choices[0]->text;
    }
}
```

As you can see in this file, we added the `getBio()`  method to our component and here are the explanations : 

**1. Get the value of the API key**

```
$key = config('services.open_ai.api_key');
```

For this, let’s add in the `.env` file a new line providing the OpenAI API key : 

```
OPENAI_API_KEY="xxxxxxxxxxxx"
```

Then, in the `config/services.php` we also need some new lines : 

```
'open_ai' => [
        'api_key' => env('OPENAI_API_KEY')
],
```

**2. Create a client that will interact with the OpenAI API**

```
$client = OpenAI::client($key);
```
**3. Call the OpenAI completions endpoint**

```
        $result = $client->completions()->create([
            'model' => 'text-davinci-003',
           'prompt' => 'Write a LinkedIn bio including the keywords: ' . $this->keywords,
            'max_tokens' => 256,
            'temperature' => 1 
        ]);
```

We create a completion for the provided prompt and parameters.

`model` parameter specifies the ID of the model to use. 
The OpenAI API is powered by a diverse set of models with different capabilities. Here, we are using  `davinci` which is the most capable GPT-3 model.

Then we create a `prompt` which we pass to the AI to complete. 

`max_tokens` is the maximum number of tokens to generate in the completion. 256 tokens lead to a maximal length of around 1000 characters. To learn more about the tokens see the [documentation](https://platform.openai.com/tokenizer).

`temperature`  provides the sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random/creative, while lower values like 0.2 will make it more focused and deterministic.

**4. Get the result**

```
$this->bio = $result->choices[0]->text;
```

We finally get our LinkedIn bio text generated by OpenAI API. 🚀

## Running the project

Before running the project, let’s register a web route to our nice page. So, open the `/routes/web.php`  and add the following lines of code : 

```
Route::get('/get-bio', function () {
    return view('home');
});

```

Great! Everything is finally ready to run our application.🥳 

Then start the server with the following command :

```
php artisan serve  
```

And run `npm run dev` for the Tailwind CSS build process. 

Now, we can access our application in the browser at `http://127.0.0.1:8000/get-bio`. 


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

## Summary

In this article, we’ve learned how to build a LinkedIn Bio Generator in Laravel using the REST API of OpenAI, Livewire and Tailwind CSS.
And as every article can be made better so your suggestions or questions are welcome in the comment section. 😉
Check the code of this tutorial [here](https://github.com/nxvv/linkedin-bio-generator).