OpenAPI or Bust: How I Made Lovable Play Nice with a Real Backend

When I first discovered Lovable, I was drawn to its promise of rapid frontend development. But like many developers, I have a custom backend I’m not willing to part with, one that gives me the security and scalability I need. So, I embarked on a journey to connect the two. It wasn’t always a straightforward path. There were moments of seamless integration and moments of frustrating roadblocks. In this post, I’ll walk you through the highs and lows of that process – what worked, what didn’t, and the key lessons I learned along the way to finally create a smooth and efficient workflow.

1. Creating an OpenAPI Specification (Biggest Win)

The biggest win in my integration journey was figuring out that AI does well with OpenAPI specifications. It is a machine-readable document that describes your backend API, including endpoints, request/response formats, authentication methods, and more. Think of it as a contract between your frontend and backend, ensuring both sides understand how to communicate effectively.

If you already have a specification, simply copy your openapi.yml or openapi.json file into the root of your Lovable project.

Though, if you are not a great documenter, you can use an AI Code Agent like Claude Code or GitHub Copilot Chat to generate one from your existing backend code. Here’s a sample prompt:

Extract the OpenAPI specification from the attached files and output it as a single openapi.yml file. Ensure that objects are
defined in the components section and referenced appropriately. Include all endpoints, request parameters, and response schemas. 
The specification should be comprehensive enough for a developer to implement the API without additional context.

Remember to attach your backend code files when running this prompt.

Once you have the openapi.yml file in your Lovable project’s root, you can generate a TypeScript API client. Run the following prompt, modifying the URL to your backend:

Interpret the openapi.yml file in the root of this project and generate a typescript API client in `lib/api.ts`. Include all 
schemas as types and interfaces. The client should have functions for each endpoint with appropriate parameters and return types. 
Use fetch for making HTTP requests and handle errors gracefully. Ensure the code is clean, well-documented, and follows best 
practices.

The backend URL is https://api.example.com

This generates an API client at lib/api.ts that you can use to interact with your backend service. To ensure that Lovable always use the API client, you should add the following the “Knowledge” section of your Lovable project:

The API client is located in lib/api.ts and should be imported to fetch any data from the backend.

Lovable Knowledge Section

What didn’t work

This method wasn’t without a rocky start. Here are some challenges I encountered prior to settling on the OpenAPI spec approach:

  • Direct Endpoint Description: I initially tried providing request and response pairs. This led to inconsistent results, as the AI struggled to generalize from specific examples.
  • Ongoing challenge: New features without endpoints and updating the OpenAPI spec. If I prompt Lovable to add a new feature that wasn’t supported in the original OpenAPI spec, it tends to hallucinate the API client code. The only way I found to combat this is to make the backend changes first, update the OpenAPI spec, and then regenerate the API client.

What could work

What I didn’t try was to directly generate the API client from the backend codebase using tools like Swagger Codegen or OpenAPI Generator. I didn’t opt in for this because I wanted to keep my client simple. Not much hallucinations happened when I used the AI generated API client.

2. Repository Management and Deployment

A common problem is having to manage 2 repositories – one for the backend and one for the Lovable frontend. This led to a lot of problems when deploying because I had to remember to deploy both repositories separately.

What worked

To keep my backend and Lovable frontend in sync, I added the Lovable GitHub repository as a submodule to my backend project. This creates a pseudo-monorepo setup, where your backend code lives in the root of the repository and the Lovable frontend lives in a subdirectory. This makes it easier to manage both codebases and coordinate changes.

To add the submodule, run these commands from your backend project’s root directory:

git submodule add https://github.com/choyiny/project-ui.git
git submodule update --init --recursive

Now, your project structure will look something like this:

/my-backend-project
  /project-ui (Lovable submodule)

Since I use Cloudflare Workers, I set up a simple build script to copy the Lovable frontend’s generated files to my backend’s public directory during deployment. Here’s a sample script to automate this:

#!/bin/bash

# Navigate into the Lovable project submodule
cd project-ui

# Pull the latest changes
git pull origin main

# Install dependencies and build the frontend
npm install
npm run build

# Remove the old public directory and copy the new build files
rm -rf ../public
mkdir -p ../public
cp -r dist/ ../public

This assumes your directory structure is:

/my-backend-project
  /project-ui (Lovable generated submodule)
    /dist (generated frontend files)
  /public (backend's public directory)

For those unfamiliar with Cloudflare Workers, you can deploy both static files and serverless functions in one place. The static files go into the public directory, while your backend logic can be handled by Workers. With hono, you can easily serve static files alongside your API routes:

const app = new Hono<{ Bindings: Bindings }>();

// your API routers
app.route("/api/users", usersRouter);

// Serve static files from the public directory
app.get("*", async (c) => {
  return c.env.ASSETS.fetch(c.req.raw);
});

export default app;

What didn’t work

What worked took 3 projects to perfect. Here are some pitfalls I encountered:

  • Separate Repositories: Initially, I kept the backend and Lovable frontend in separate repositories. This led to synchronization issues, as changes in one repository often required corresponding changes in the other. It became cumbersome to manage deployments and ensure both parts were up-to-date.
  • Copy and Paste: As I wanted to separate the two repositories, I tried copying the generated Lovable files into my backend’s public directory manually. This was error-prone and tedious, especially when I had to remember to do it every time I made changes to the frontend.
  • Monorepo Approach: I attempted to bring both the backend and Lovable frontend into a single monorepo. However, Lovable was having a lot of trouble installing my backend dependencies, leading to weird preview errors on Lovable. This approach was ultimately abandoned.

3. Handling Authentication

One of the best features of Lovable is its instant preview. However, if your backend requires authentication, you need to ensure your Lovable frontend can handle it within the preview environment, which is typically an iframe. Some methods, like “Login with Google” or one-time links, can be challenging here. I found two reliable ways to handle this.

Use localStorage and Bearer Tokens

This is the simplest way to manage authentication with Lovable. After a user logs in, you can store a Bearer token in localStorage. Then, modify your lib/api.ts client to automatically include this token in the Authorization header of all your API requests. This approach works well and is straightforward to implement.

Use Cookies with SameSite=None and Secure Flags (on Staging only)

If you need to use cookies for authentication, set them with the SameSite=None and Secure flags. This allows the browser to send the cookies in cross-site requests, which is essential for Lovable’s iframe environment.

Be extremely careful with this approach, as it can make your backend vulnerable to Cross-Site Request Forgery (CSRF) attacks. It’s best to use this method only with your staging API and never with your production API. You can mitigate this risk by using anti-CSRF tokens if your backend framework supports them.

What didn’t work

This was another trial-and-error process. Here are some methods I tried that didn’t work well:

  • Trying to get OAuth flows to work within the Lovable preview iframe. Most OAuth providers block third-party cookies, making it impossible to complete the authentication process.
  • A username & password login bypass within the Lovable preview. This was a security risk and not a scalable solution.
  • Mocking all authentication in the Lovable preview. This led to discrepancies between the preview and production environments, causing confusion during development. Furthermore, it added a lot of complexity to lib/api.ts, as I had to handle both real and mocked authentication flows.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.