Degoog Docs

Degoog Autocomplete

Build your own autocomplete providers to power the search-bar suggestion dropdown. Providers are aggregated, deduplicated, and ranked automatically.

degoog-cli is now available. Scaffold autocomplete provider boilerplate directly from your terminal - no copy-pasting, no guessing the folder structure. Install with curl -fsSL https://raw.githubusercontent.com/degoog-org/cli/main/install.sh | sh or grab it from github.com/degoog-org/cli.

Where providers live

Drop your custom provider into data/autocomplete/ or whatever you set DEGOOG_AUTOCOMPLETE_DIR to. Each provider is a file or folder with an entry file that exports a class or object containing a name and getSuggestions.

Provider contract

Required:

  • name: The display name shown in Settings → Autocomplete.
  • getSuggestions(query, context) (async): Returns a Promise<AutocompleteSuggestion[]>. The array should contain plain suggestion strings or rich suggestion objects ({ text: string, rich?: RichSuggestion }). Return an empty array if no suggestions are available - never throw.

Optional:

  • settingsSchema and configure(settings): Declare settings fields and receive saved values on startup and after every save. Works exactly like engines and plugins. See Search engines for the full SettingField shape reference.

The context object

Every call to getSuggestions receives a context argument. Always use context.fetch for outbound HTTP so the provider respects proxy and transport settings.

Field Type Description
context.fetch typeof fetch A proxy- and transport-aware fetch function. Use this for every outbound HTTP request instead of the global fetch.
context.lang string ISO 639-1 base language code (e.g. "en", "fr", "de"). Derived from the DEGOOG_DEFAULT_SEARCH_LANGUAGE environment variable. Defaults to "en" when the variable is unset.
context.useCache UseCache Async, namespaced TTL cache factory. Call context.useCache<T>(namespace, defaultTtlMs) to get a cache with await get, await set, await delete, and await clear. Shared across replicas when the optional Valkey sidecar is configured; per-process otherwise.

Rich suggestions

Providers can return entity cards (images, subtitles, and descriptions) that appear at the very top of the autocomplete dropdown. To do this, return an object instead of a string:

export interface RichSuggestion {
  description?: string;
  thumbnail?: string;
  type?: string;
}

export type AutocompleteSuggestion = string | { text: string; rich?: RichSuggestion };

Plain string suggestions are fetched on the server via context.fetch (which respects the provider's outgoing transport). When you include rich.thumbnail, return the upstream image URL from getSuggestions. Degoog rewrites it to a signed /api/proxy/image URL before the suggest API responds, so the dropdown loads the image through the server.

Declaring client exposure

The settings page shows a network indicator on plugin cards when an extension declares isClientExposed. Autocomplete providers do not use that field. Return upstream thumbnail URLs as usual; Degoog signs them for the image proxy when suggestions are merged.

What you return Server requests Browser requests
Plain strings only Your provider uses context.fetch to call upstream APIs. None from the provider itself.
Rich suggestions with thumbnail Your provider fetches suggestions; Degoog signs each thumbnail for /api/proxy/image. The browser loads the signed proxy URL only. Degoog fetches the upstream image on the server.

You do not need to call signProxyUrl or build proxy URLs yourself in a provider. Omit rich.thumbnail entirely if you do not want image cards in the dropdown.

async getSuggestions(query, context) {
  const doFetch = context?.fetch ?? fetch;
  const res = await doFetch(`https://example.com/suggest?q=${encodeURIComponent(query)}`);
  const items = await res.json();
  return items.map((item) => ({
    text: item.title,
    rich: {
      description: item.subtitle,
      thumbnail: item.imageUrl,
    },
  }));
}
async getSuggestions(query, context) {
  const doFetch = context?.fetch ?? fetch;
  const res = await doFetch(`https://example.com/suggest?q=${encodeURIComponent(query)}`);
  const items = await res.json();
  return items.map((item) => item.title);
}

Minimal example

export default class MyAutocompleteProvider {
  name = "My Provider";

  async getSuggestions(query, context) {
    if (!query.trim()) return [];
    const doFetch = context?.fetch ?? fetch;
    const encoded = encodeURIComponent(query.trim());
    try {
      const res = await doFetch(
        `https://example.com/suggest?q=${encoded}`,
        { headers: { Accept: "application/json" } },
      );
      if (!res.ok) return [];
      const data = await res.json();
      // OpenSearch format: [query, [suggestion, suggestion, ...]]
      if (Array.isArray(data) && Array.isArray(data[1])) {
        return data[1].map(String).filter(Boolean);
      }
      return [];
    } catch {
      return [];
    }
  }
}

OpenSearch compatibility

The /api/suggest/opensearch endpoint returns results in the OpenSearch Suggestions 1.1 format: [query, [suggestion, suggestion, ...]]. This means your browser address bar can use Degoog directly as a search engine with autocomplete. Custom providers do not need to do anything special - the registry wraps all results automatically.

Aggregation and scoring

When multiple providers are enabled, their suggestions are fetched in parallel and merged using a round-robin interleave. Duplicates are collapsed into a single suggestion that shows all contributing sources. The merged list is capped at 10 items.

Each provider has a Score field under the Advanced section in Settings → Autocomplete. Higher scores mean that provider's suggestions appear earlier in the merged list. The default score is 1.

Outgoing transport

Like engines, every provider automatically gets an Outgoing HTTP client selector under Advanced. This can be set to fetch, curl, curl-fallback, or any custom transport you have installed. Always use context.fetch in your provider so this setting applies automatically.

Settings example

export default class MyAutocompleteProvider {
  name = "My Provider";

  settingsSchema = [
    {
      key: "apiKey",
      label: "API Key",
      type: "password",
      secret: true,
      required: false,
      description: "Optional API key for authenticated requests.",
    },
  ];

  apiKey = "";

  configure(settings) {
    this.apiKey = typeof settings.apiKey === "string" ? settings.apiKey.trim() : "";
  }

  async getSuggestions(query, context) {
    if (!query.trim()) return [];
    const doFetch = context?.fetch ?? fetch;
    const encoded = encodeURIComponent(query.trim());
    const headers = { Accept: "application/json" };
    if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}`;
    try {
      const res = await doFetch(`https://example.com/suggest?q=${encoded}`, { headers });
      if (!res.ok) return [];
      const data = await res.json();
      return Array.isArray(data) ? data.map(String).filter(Boolean) : [];
    } catch {
      return [];
    }
  }
}

Language

context.lang is always set to a two-letter ISO 639-1 code. It is derived from the DEGOOG_DEFAULT_SEARCH_LANGUAGE environment variable (e.g. en-GB becomes en). If the variable is not set, context.lang defaults to "en". Use it to pass language or market parameters to your upstream API.

async getSuggestions(query, context) {
  const lang = context?.lang ?? "en";
  const mkt = `${lang}-${lang.toUpperCase()}`; // e.g. "en-EN", "fr-FR"
  const res = await (context?.fetch ?? fetch)(
    `https://example.com/suggest?q=${encodeURIComponent(query)}&lang=${lang}`,
  );
  // ...
}

Setup

Create your data/autocomplete/ directory or set DEGOOG_AUTOCOMPLETE_DIR. Add a single file like my-provider.js or a folder with an index.js. The provider ID will be the filename or folder name prefixed with autocomplete-.

If you plan on distributing your provider through the Store, add it under the autocomplete key in your store's package.json manifest alongside a screenshots/ folder so the Store card looks great.

Built-in providers

Degoog ships two built-in providers that are always available and cannot be uninstalled:

  • Google - uses the Firefox suggest endpoint.
  • DuckDuckGo - uses the public DuckDuckGo autocomplete API.

Additional providers (Brave, Bing, Yahoo) are available from the official store under the Autocomplete category.