Skip to content

Integrate

Integrate an analytics assistant into your application with Inconvo.

Build your in-app analytics assistant using Inconvo, a backend API that manages database connection, semantic translation, multi-tennancy and more.

This example uses a React frontend and a Node.js backend, but you can use any frontend and backend technology you like.

  1. Add an endpoint on your server to create a conversation.

    POST /api/v1/conversations responds with an id which is used to create conversation responses.

    app.post("/create-conversation", async (_req, res) => {
    // This is placeholder context,
    // You should set your own based on the request
    const context = {
    organisationId: 1,
    };
    try {
    const response = await fetch(
    `${process.env.INCONVO_API_BASE_URL}/conversations/`,
    {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.INCONVO_API_KEY}`,
    },
    body: JSON.stringify({
    context,
    }),
    }
    );
    if (!response.ok) {
    throw new Error(`Failed to create conversation: ${response.status}`);
    }
    const data = await response.json();
    res.json(data);
    } catch (error) {
    console.error("Error creating conversation:", error);
    res.status(500).json({ error: "Failed to create conversation" });
    }
    });

    Add an endpoint on your server to create a response.

    POST /api/v1/conversations/answer responds with an answer which includes:

    • An id which you can use when attaching feedback to an answer.
    • A conversationId which you can optionally pass to the next call to continue the conversation.

    See the response type reference here.

    Return the answer in your response.

    app.post("/create-response", async (req, res) => {
    const { message, conversationId } = req.body;
    try {
    const response = await fetch(
    `${process.env.INCONVO_API_BASE_URL}/conversations/${conversationId}/response/`,
    {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.INCONVO_API_KEY}`,
    },
    body: JSON.stringify({
    message,
    }),
    }
    );
    if (!response.ok) {
    throw new Error(`Failed to create response: ${response.status}`);
    }
    const inconvoResponse = await response.json();
    res.json(inconvoResponse);
    } catch (error) {
    console.error("Error from Inconvo AI:", error);
    res.status(500).json({ error: "Failed to get response from Inconvo AI" });
    }
    });
  2. Initialize some state to keep track of the message-response pairs, and manage the user interface.

    const Assistant = () => {
    const [message, setMessage] = useState("");
    const [messageResponsePairs, setMessageResponsePairs] = useState([]);
    const [conversationId, setConversationId] = useState(null);
    const [isLoading, setIsLoading] = useState(false);
    }

    const createNewConversation = async () => {
    setConversationId(null);
    setMessage("");
    setMessageResponsePairs([]);
    try {
    const res = await fetch(`http://localhost:4242/create-conversation`, {
    method: "POST",
    });
    const conversation = await res.json();
    setConversationId(conversation.id);
    } catch (err) {
    console.error("Error creating conversation:", err);
    }
    };

    const MessageInput = ({ message, setMessage, isLoading, conversationId }) => {
    const handleChange = (e) => {
    setMessage(e.target.value);
    };
    return (
    <label>
    Enter message:
    <input
    id="message"
    type="text"
    disabled={!conversationId || isLoading}
    value={message}
    onChange={handleChange}
    />
    </label>
    );
    };

    Put the MessageInput and submit button in a form.

    <form onSubmit={handleSubmit}>
    <MessageInput
    message={message}
    setMessage={setMessage}
    isLoading={isLoading}
    conversationId={conversationId}
    />
    <button disabled={isLoading || !conversationId} id="submit">
    {isLoading ? `Thinking ...` : `Submit`}
    </button>
    </form>

    Listen to the form’s submit event to know when to send the users’ message to your server which will securely create a response using the Inconvo API.

    Take the message from the response and add it to the messageResponsePairs array.

    const handleSubmit = async (e) => {
    e.preventDefault();
    setIsLoading(true);
    try {
    const res = await fetch(`http://localhost:4242/create-response`, {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    },
    body: JSON.stringify({
    message,
    ...(conversationId ? { conversationId } : {}),
    }),
    });
    if (!res.ok) {
    throw new Error(`Server responded with status: ${res.status}`);
    }
    const data = await res.json();
    if (data.conversationId && !conversationId) {
    setConversationId(data.conversationId);
    }
    setMessageResponsePairs((prevMessageResponsePairs) => [
    ...prevMessageResponsePairs,
    { message, response: data },
    ]);
    } catch (err) {
    console.error("Error submitting message:", err);
    } finally {
    setIsLoading(false);
    setMessage("");
    }
    };

    Create a component to render the response. This component takes an Inconvo Answer and renders it based on its type [text, chart, table].

    const ResponseOutput = ({ response }) => {
    if (!response || Object.keys(response).length === 0) {
    return <div>Send a message to see a response here</div>;
    }
    switch (response.type) {
    case "text":
    return <div>{response.message}</div>;
    case "table":
    return (
    <table>
    <caption>{response.message}</caption>
    <thead>
    <tr>
    {response.table.head.map((h, i) => (
    <th key={i}>{h}</th>
    ))}
    </tr>
    </thead>
    <tbody>
    {response.table.body.map((row, i) => (
    <tr key={i}>
    {row.map((cell, j) => (
    <td key={j}>{cell}</td>
    ))}
    </tr>
    ))}
    </tbody>
    </table>
    );
    case "chart": {
    const data = response.chart.data.map((item) => [item.label, item.value]);
    switch (response.chart.type) {
    case "bar":
    return (
    <div className="chart-container">
    <div>{response.message}</div>
    <BarChart data={data} round={2} thousands="," width="400px" />
    </div>
    );
    case "line":
    return (
    <div className="chart-container">
    <div>{response.message}</div>
    <LineChart data={data} round={2} thousands="," width="400px" />
    </div>
    );
    default:
    return <div>Unsupported chart type</div>;
    }
    }
    default:
    return <div>Unsupported response type</div>;
    }
    };

    Finally, display the conversation by mapping over the messageResponsePairs state and rendering the question and answer.

    <section>
    {messageResponsePairs.length > 0 && (
    <div className="conversation-history">
    {messageResponsePairs.map((messageResponsePair, index) => (
    <div key={index} className="message-response-pair">
    <div className="message-container">
    <p className="message">{messageResponsePair.message}</p>
    </div>
    <div className="response-container">
    <ResponseOutput response={messageResponsePair.response} />
    </div>
    </div>
    ))}
    </div>
    )}
    </section>