> For the complete documentation index, see [llms.txt](https://adiom.gitbook.io/data-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adiom.gitbook.io/data-api/tutorials/building-web-applications.md).

# Building Web Applications

You can use the [Connect RPC](https://connectrpc.com/docs/introduction) library to connect to DAPI directly from web or mobile applications.&#x20;

If you're using a code generation tool like Claude, CoPilot, Replit, Cursor, or Vercel, DAPI significantly simplifies the data layer and streamlines the development by ensuring that your data model is in one place and doesn't leak across the code base.

{% hint style="info" %}
You can find a complete and very helpful tutorial [here](https://dev.to/adkomyagin/vibe-code-like-a-pro-build-an-mvp-web-app-with-mongodb-atlas-in-1-hour-using-cursor-4i2k).
{% endhint %}

<figure><img src="https://4211308970-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FB2xwJ3fa5RUPg0ZyxPiH%2Fuploads%2FgCLwLklfMcnupuHm50j9%2Fimage.png?alt=media&amp;token=2fddb51e-6af9-4ce3-aa7d-59f29b43ceaa" alt=""><figcaption></figcaption></figure>

### Generation prompt

In our experiments, we had great success with one-shot generation of a React TypeScript web application with ChatGPT and the following prompt:

{% code overflow="wrap" %}

```
Given the following protobuf definition for a data backend API server running at grpc://<GRPC_SERVER> and the authentication headers (-H "dapi-key: <DAPI_KEY>" -H "authorization: Bearer <AUTH_TOKEN>"), generate a React web app that lists all movies and allows to see comments for a movie that the user selects. Use Connect RPC to communicate with the gRPC backend. 
syntax = "proto3";

package a.example;

option go_package = "github.com/adiom-data/dapi/gen";

import "google/protobuf/timestamp.proto";

service MovieService {
    // List available movies
    rpc List(ListRequest) returns (ListResp);
    // Get a movie with all comments
    rpc GetMovieWithComments(GetMovieWithCommentsRequest) returns (GetMovieWithCommentsResp);
}

message ListRequest {
}

message ListResp {
    repeated Movie data = 1;
    Movie movie = 2;
}

message GetMovieWithCommentsRequest {
    string title = 1;
}

message GetMovieWithCommentsResp {
    repeated MovieWithComments data = 1;
    Movie MovieWithComments = 2;
}

message Comment {
    string id = 1;
    string text = 2;
    string name = 3;
    string email = 4;
    google.protobuf.Timestamp date = 5;
    string movie_id = 6;
}

message Movie {
    string id = 1;
    string title = 2;
    string plot = 3;
    int32 runtime_seconds = 5;
    google.protobuf.Timestamp released = 6;
    string fullplot = 7;
    string poster = 8;
}

message MovieWithComments {
    string id = 1;
    string title = 2;
    string plot = 3;
    repeated Comment comments = 4;
}

Use the following example for generating code from the protobuf definition:

$ npm install --save-dev @bufbuild/buf @bufbuild/protoc-gen-es
$ npm install @connectrpc/connect @connectrpc/connect-web @bufbuild/protobuf

# buf.yaml
version: v2
modules:
  - path: proto
lint:
  use:
    - DEFAULT
breaking:
  use:
    - FILE

# buf.gen.yaml defines a local generation template.
# For details, see https://buf.build/docs/configuration/v2/buf-gen-yaml
version: v2
plugins:
  # This will invoke protoc-gen-es and write output to src/gen
  - local: protoc-gen-es
    out: src/gen
    # Also generate any imported dependencies
    include_imports: true
    # Add more plugin options here
    opt: target=ts


Use the following example for connecting to the RPC client:
// use-client.ts
import { useMemo } from "react";
import { type DescService } from "@bufbuild/protobuf";
import { createConnectTransport } from "@connectrpc/connect-web";
import { createClient, type Client } from "@connectrpc/connect";

// This transport is going to be used throughout the app
const transport = createConnectTransport({
  baseUrl: "https://demo.connectrpc.com",
});

/**
* Get a promise client for the given service.
*/
export function useClient<T extends DescService>(service: T): Client<T> {
  // We memoize the client, so that we only create one instance per service.
  return useMemo(() => createClient(service, transport), [service]);
}
```

{% endcode %}

### CORS settings (only for self-hosted DAPI)

Accessing the service from web browsers requires [CORS](/data-api/getting-started/concepts.md#cors) configuration in the yaml file, for example:

```
interceptors:
  - name: CORS
    config:
      allowedorigins: ["*"]
      allowedheaders: ["Authorization","Dapi-Key"]
```
