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-answer", async (req, res) => {
    const { question, conversationId } = req.body;
    // NOTE: Hardcoded placeholder context for sample data.
    // Fetch this from a trusted source in your application.
    const payload = {
    question,
    context: {
    organisationId: 1
    }
    }
    if (conversationId) {
    payload.conversationId = conversationId
    }
    try {
    const response = await fetch("https://app.inconvo.ai/api/v1/conversations/answer", {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.INCONVO_API_KEY}`,
    },
    body: JSON.stringify(payload),
    });
    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’ question to your server which will securely Create an answer 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-ask`, {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    },
    body: JSON.stringify({
    question,
    ...(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);
    }
    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>