npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@florentine-ai/api

v1.0.0

Published

Node.js API Wrapper for Florentine.ai – query MongoDB & MySQL databases in natural language

Downloads

6,414

Readme

Florentine.ai API - Talk to your MongoDB & MySQL data

The Florentine.ai API lets you integrate natural language querying for your MongoDB & MySQL data directly into your project.

Questions are transformed into database queries that can also directly be executed and answered in natural language.

Also has a couple of extra features under the hood, e.g.:

  • Secure data separation for multi-tenant usage
  • Automated schema exploration
  • Semantic vector search/RAG support with automated embedding creation
  • Advanced lookup support
  • Exclusion of keys

[!NOTE] If you are looking for our MCP Server you can find it here.

Contents

Prerequisites

  • Node.js >= v18.0.0
  • A Florentine.ai account (create a free account here)
  • A connected database and at least one analyzed and activated collection/table in your Florentine.ai account
  • A Florentine.ai API Key (you can find yours on your account dashboard)

Installation

A detailed documentation of the API can be found here in our docs.

npm install @florentine-ai/api

Authentication

import { Florentine } from '@florentine-ai/api';

const FlorentineAI = new Florentine({
  florentineToken: '<FLORENTINE_API_KEY>'
});

Connect your LLM account

Florentine.ai works as a bring your own key model, so you need to provide your LLM API key (OpenAI, Google, Anthropic, Deepseek) in your API requests.

You have two options how you can add your LLM API key:

Option 1: Save your LLM key in your account (recommended)

The easiest way to connect to your LLM provider is to save your LLM API key in your Florentine.ai dashboard.

  • Add your API key
  • Select your LLM provider (OpenAI, Deepseek, Google or Anthropic)
  • Click Save

Add your LLM key

Option 2: Provide your LLM key in API requests

If you prefer not to store the key in your Florentine.ai account or want to use multiple LLM keys, you can pass the key in your API request.

Add the values as llmService and llmKey to the class constructor object:

const FlorentineAI = new Florentine({
  florentineToken: '<FLORENTINE_API_KEY>',
  llmService: '<YOUR_LLM_SERVICE>', // one of: "openai", "google", "anthropic", "deepseek"
  llmKey: '<YOUR_LLM_API_KEY>'
});

[!NOTE] If you provide an llmKey inside the request, it will override any key stored in your account.

First Request

You should now be ready to send your first API request!

Come up with a question that can be answered via the data in your activated collection(s)/table(s) and pass it to the .ask() method:

const FlorentineAI = new Florentine({
  florentineToken: '<FLORENTINE_API_KEY>'
});

const res = await FlorentineAI.ask({
  question: '<YOUR_QUESTION>',
  sessionId: 'FIRST_REQUEST'
});

By default, the API returns the raw query result. The return type can be configured via returnTypes.

Imagine a tabletennis collection that records the results of the matches of two players. If I ask the question Who won the last match? the answer might look similar to this:

{
  "answer": "Frank won the last match. According to our records, he emerged as the winner, highlighting his strong performance in that game."
}

Return Types

In the background, three steps happen for every request:

  1. Query Generation: The question is converted into a database query (MongoDB aggregation pipeline or MySQL query).
  2. Query Execution: The query runs against the database using the connection string you provided.
  3. Answer Generation: The structured result is transformed into a natural language answer.

The default return type is configured in your Florentine.ai account. You can override it per request by specifying a returnTypes array with any combination of:

| returnTypes Value | Description | Expected Keys in Response | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | "query" | Returns the generated database query, the database and collection/table used, a confidence score on a scale from 0 to 10 and the database type ("mongodb" or "mysql"). | confidence, database, collection, query, databaseType | | "result" | Returns the raw query results from the executed query. | result | | "answer" | Returns a natural language response based on the results from the executed query. | answer |

Example returning all three steps

Let's imagine the tabletennis example from the first request section once more.With all three steps the request looks like this:

const res = await FlorentineAI.ask({
  question: 'Who won the last match?',
  returnTypes: ['query', 'result', 'answer']
});

And the response looks like this:

{
  "database": "samples",
  "collection": "tabletennis",
  "databaseType": "mongodb",
  "query": [
    { "$sort": { "year": -1, "matchInYear": -1 } },
    { "$limit": 1 },
    {
      "$project": {
        "winner": {
          "$cond": {
            "if": { "$eq": ["$matchwinner", "home"] },
            "then": "$players.home",
            "else": "$players.away"
          }
        }
      }
    }
  ],
  "result": [{ "_id": "67d352056d3ef0f2281524cf", "winner": "Frank" }],
  "answer": "Frank won the last match. According to our records, he emerged as the winner, highlighting his strong performance in that game."
}

Secure Data Separation for multi-tenant usage

You can enable secure data separation by ensuring queries filter data based on provided values which we call Required Inputs.

These values are added to the query by the Florentine.ai transformation layer after the query generation by the LLM. Thus Florentine.ai can assure each user only retrieves the data he is eligible to.

Keys are defined as Required Input in your account, please refer to the section in our official docs on how to do that.

Providing Required Inputs

Provide your Required Inputs inside a requiredInputs array with keyPath and value for each key you set as Required Input and add it to the .ask() method:

const res = await FlorentineAI.ask({
  question: 'What is the revenue of my products this year?',
  requiredInputs: [
    {
      keyPath: 'accountId',
      value: '507f1f77bcf86cd799439011'
    }
  ]
});

You may also provide a database and a collections array in case you have Required Inputs with the same keyPath in multiple collections/tables but different value for the collections/tables:

const res = await FlorentineAI.ask({
  question: 'Whats the current monthly rent of my tenants?',
  requiredInputs: [
    {
      keyPath: 'name',
      value: 'Sesame Street',
      database: 'rentals',
      collections: ['houses']
    },
    {
      keyPath: 'name',
      value: { $in: ['Ernie', 'Bert'] },
      database: 'rentals',
      collections: ['tenants']
    }
  ]
});

Required Inputs Configuration

| Field | Required | Type | Description | Constraints | | ------------- | -------- | --------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | keyPath | Yes | String | The path to the field that should be filtered. | Must be a valid key path. | | value | Yes | Any | The value(s) to filter by (type-specific, see Supported Value Types). | Must match the field's type (String, ObjectId, Boolean, Number, or Date). | | database | No | String | The database containing the collections to filter. | Must be provided if collections is provided. | | collections | No | Array<String> | The specific collections/tables within the database to apply the filter to. | Must contain at least one collection/table. |

Supported Value Types

Based on the type of the values for the key you have different options on what you can provide as a Required Input value:

| Type | Format Examples | Operators Supported | Notes | | ------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------- | | String or Array<String> | "text"{ $in: ["text1", "text2"] } | $in | Case-sensitive. | | ObjectId or Array<ObjectId> | "507f191e810c19729de860ea"{ $in: ["507f191e810c19729de860ea", "507f191e810c19729de860eb"] } | $in | Provided as strings. | | Boolean | true/false | — | Only exact values. | | Number or Array<Number> | 42{ $gt: 10, $lte: 100 }{ $in: [1, 2, 3] }{ $in: [{$gte:1}, {$lt:10}] } | $gt, $gte, $lt, $lte, $in | Supports decimals. | | Date or Array<Date> | "2024-01-01T00:00:00Z" (UTC)"2024-01-01T00:00:00-05:00"(timezone offset) | $gt, $gte, $lt, $lte, $in | ISO 8601 format. |

Usage Examples

Example type: String

Usecase: A user should only be able to see statistics of the players he frequently plays with.

Solution: Restricting access by player name to a group of 4 players.

const res = await FlorentineAI.ask({
  question: 'Which player had the most wins?',
  requiredInputs: [
    {
      keyPath: 'name',
      value: { $in: ['Megan', 'Frank', 'Jen', 'Bob'] }
    }
  ]
});

Example type: ObjectId

Usecase: A user should only be able to see the revenue of his own products.

Solution: Restricting the access by the accountId to one specific account.

const res = await FlorentineAI.ask({
  question: 'Whats the revenue of my products?',
  requiredInputs: [
    {
      keyPath: 'accountId',
      value: '507f1f77bcf86cd799439011'
    }
  ]
});

Example type: Boolean

Usecase: Every analysis of customers should only be performed on paying customers.

Solution: Restricting the access by isPaidAccount to paying customers only.

const res = await FlorentineAI.ask({
  question: 'How many customers registered in the last year?',
  requiredInputs: [
    {
      keyPath: 'isPaidAccount',
      value: true
    }
  ]
});

Example type: Number

Usecase: An employee should only be allowed to see payment information for payments below a certain amount.

Solution: Restricting the access by amount to payments below 10.000.

const res = await FlorentineAI.ask({
  question: 'List all payments we received.',
  requiredInputs: [
    {
      keyPath: 'amount',
      value: { $lt: 10000 }
    }
  ]
});

Example type: Date

Usecase: The analysis of financial data should only include one specific year.

Solution: Restricting the access by transactionDate to all transactions in 2024.

const res = await FlorentineAI.ask({
  question: 'What was our revenue, profit and margin per month?',
  requiredInputs: [
    {
      keyPath: 'transactionDate',
      value: {
        $gte: '2023-01-01T00:00:00Z',
        $lt: '2024-01-01T00:00:00Z'
      }
    }
  ]
});

Sessions

Sessions allow Florentine.ai to enable a server-side chat history. Just provide a sessionId string like this:

const res = await FlorentineAI.ask({
  question: 'What is the revenue of my products this year?',
  sessionId: '<YOUR_SESSION_ID>'
});

Sessions are mandatory unless you disable the server-side chat history by setting withHistory to false inside the constructor:

const FlorentineAI = new Florentine({
  florentineToken: '<FLORENTINE_API_KEY>',
  withHistory: false
});

[!NOTE] It is strongly recommended to use a sessionId to have meaningful multi-turn conversations.

Errors

All errors from the Florentine.ai API follow this consistent JSON structure:

{
  "error": {
    "name": "FlorentineApiError",
    "statusCode": 401,
    "message": "Please provide your Florentine API key. You can find it in your account settings: https://florentine.ai/settings",
    "errorCode": "NO_TOKEN",
    "requestId": "abc123"
  }
}

| Field | Type | Description | | ------------ | ------ | ------------------------------------------------------------------------- | | name | string | Error class name (e.g. FlorentineApiError, FlorentineConnectionError) | | statusCode | number | HTTP status code (e.g. 400, 500) | | message | string | Explanation of what went wrong | | errorCode | string | Error identifier (e.g. NO_TOKEN, INVALID_LLM_KEY) | | requestId | string | Unique ID for this request (helpful for support and debugging) |

Handle errors in client

API errors are automatically thrown as JavaScript error classes:

import { Florentine, FlorentineApiError } from '@florentine-ai/api';

try {
  const FlorentineAI = new Florentine({});
  const res: TFlorentineReturnOutput = await FlorentineAI.ask({
    question: 'Who is Florentine?'
  });
} catch (err) {
  if (err instanceof FlorentineApiError) {
    console.error(err.message); // "Please provide your Florentine API key."
    console.error(err.statusCode); // 401
    console.error(err.errorCode); // "NO_TOKEN"
    console.error(err.requestId); // "abc123"
  }
}

All client errors extend the FlorentineError class, so you can catch all client related errors like this:

if (err instanceof FlorentineError) {
  // Catches all florentine npm package errors
}

Common Errors

| Error Type | errorCode | Meaning | | --------------------------- | ------------------------------- | ------------------------------------------------------------------ | | FlorentineApiError | NO_TOKEN | The Florentine API key is missing | | FlorentineApiError | INVALID_TOKEN | The Florentine API key is invalid | | FlorentineApiError | LLM_KEY_WITHOUT_SERVICE | You must provide a llmService if llmKey is defined | | FlorentineApiError | LLM_SERVICE_WITHOUT_KEY | You must provide a llmKey if llmService is defined | | FlorentineApiError | INVALID_LLM_SERVICE | Invalid llmService provided | | FlorentineApiError | NO_OWN_LLM_KEY | You need to provide your own llm key | | FlorentineApiError | NO_ACTIVE_COLLECTIONS | No collections/tables activated for the account | | FlorentineApiError | MISSING_REQUIRED_INPUT | Required input is missing | | FlorentineApiError | INVALID_REQUIRED_INPUT | Required input is invalid | | FlorentineApiError | INVALID_REQUIRED_INPUT_FORMAT | Required input format is invalid | | FlorentineApiError | NO_QUESTION | Question is missing | | FlorentineApiError | EXECUTION_FAILURE | Created query execution failed | | FlorentineApiError | NO_CHAT_ID | History chat id required but missing | | FlorentineLLMError | API_KEY_ISSUE | LLM API key is invalid | | FlorentineLLMError | NO_RETURN | Florentine.ai did not receive a valid LLM return | | FlorentineLLMError | RATE_LIMIT_EXCEEDED | LLM Request size too big | | FlorentineApiError | TOO_MANY_TOKENS | The query prompt exceeds the maximum tokens of the LLM model | | FlorentineConnectionError | CONNECTION_REFUSED | Could not connect to database for query execution | | FlorentineCollectionError | NO_EXECUTION | Created query could not be executed | | FlorentinePipelineError | MODIFICATION_FAILED | Modifying the query pipeline failed | | FlorentineUsageError | LIMIT_REACHED | All API requests included in your plan depleted | | FlorentineUnknownError | UNKNOWN_ERROR | All occurring unknown errors |