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 an Answer.

    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;
    // Use the conversationId in the URL path
    const url = `https://app.inconvo.ai/api/v1/conversations/${conversationId}/response`;
    try {
    const response = await fetch(url, {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.INCONVO_API_KEY}`,
    },
    body: JSON.stringify(message),
    });
    const answer = await response.json();
    res.json(answer);
    } catch (error) {
    console.error("Error from Inconvo:", error);
    res.status(500).json({ error: "Failed to get response from Inconvo" });
    }
    });
  2. Build an assistant interface on the client

    Section titled “Build an assistant interface on the client”

    Initialize some state to keep track of the question/answer pairs, and manage the user interface.

    const Assistant = () => {
    const [question, setQuestion] = useState("");
    const [qaPairs, setQaPairs] = useState([]);
    const [conversationId, setConversationId] = useState(null);
    const [isLoading, setIsLoading] = useState(false);
    ...
    }

    const QuestionInput = ({ question, setQuestion, isLoading }) => {
    const handleChange = (e) => {
    setQuestion(e.target.value);
    };
    return (
    <label>
    Enter your question:
    <input
    id="question"
    type="text"
    disabled={isLoading}
    value={question}
    onChange={handleChange}
    placeholder="What is our most popular product?"
    />
    </label>
    );
    };

    Put the QuestionInput and submit button in a form.

    <form onSubmit={handleSubmit}>
    <QuestionInput
    question={question}
    setQuestion={setQuestion}
    isLoading={isLoading}
    />
    <button disabled={isLoading} id="submit">
    {isLoading ? `Thinking ...` : `Ask`}
    </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 answer from the response and add it to the qaPairs 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: question,
    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);
    }
    setQaPairs((prevQaPairs) => [...prevQaPairs, { question, answer: data }]);
    } catch (err) {
    console.error("Error submitting question:", err);
    } finally {
    setIsLoading(false);
    setQuestion("");
    }
    };

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

    const AnswerOutput = ({ answer }) => {
    switch (answer.type) {
    case "text":
    return <div>{answer.message}</div>;
    case "table":
    return (
    <table>
    <caption>{answer.message}</caption>
    <thead>
    <tr>
    {answer.table.head.map((h, i) => (
    <th key={i}>{h}</th>
    ))}
    </tr>
    </thead>
    <tbody>
    {answer.table.body.map((row, i) => (
    <tr key={i}>
    {row.map((cell, j) => (
    <td key={j}>{cell}</td>
    ))}
    </tr>
    ))}
    </tbody>
    </table>
    );
    case "chart": {
    const data = answer.chart.data.map((item) => [item.label, item.value]);
    switch (answer.chart.type) {
    case "bar":
    return (
    <div className="chart-container">
    <div>{answer.message}</div>
    <BarChart data={data} round={2} thousands="," width="400px" />
    </div>
    );
    case "line":
    return (
    <div className="chart-container">
    <div>{answer.message}</div>
    <LineChart data={data} round={2} thousands="," width="400px" />
    </div>
    );
    default:
    return <div>Unsupported chart type</div>;
    }
    }
    default:
    return <div>Unsupported answer type</div>;
    }
    };

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

    <section>
    {qaPairs.length > 0 && (
    <div>
    {qaPairs.map((qaPair, index) => (
    <div key={index}>
    <div>
    <p>{qaPair.question}</p>
    </div>
    <div>
    <AnswerOutput answer={qaPair.answer} />
    </div>
    </div>
    ))}
    </div>
    )}
    </section>