GET /api/search
Full-text search endpoint using MiniSearch
Search API Specification
This document specifies the GET /api/search Netlify serverless function for full-text search over site content.
Overview
| Property | Value |
|---|---|
| Endpoint | GET /api/search |
| Authentication | None (public) |
| Rate Limiting | None (Netlify default) |
| Caching | Cache-Control: public, max-age=300 (5 minutes) |
| CORS | Access-Control-Allow-Origin: * |
| Rewrite Rule | [[redirects]] in netlify.toml: /api/search -> /.netlify/functions/search |
Request
Method
GET
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | Yes | — | Search query (trimmed, max 200 characters) |
excerpt_length | number | No | 200 | Max length of excerpt in response (range: 50-500) |
Example
GET /api/search?q=eurorack+power&excerpt_length=300
Response
Success (200)
{
"results": [
{
"id": "guides/col007-power-vol3",
"slug": "/guides/col007-power-vol3/",
"title": "Eurorack Power Guide Vol.3",
"description": "Power distribution best practices",
"tags": ["power", "eurorack"],
"createdAt": "2025-06-15",
"excerpt": "...truncated plain text around matching terms...",
"highlighted_title": "<mark>Eurorack</mark> <mark>Power</mark> Guide Vol.3",
"highlighted_excerpt": "...text with <mark>matching</mark> terms highlighted...",
"score": 12.345
}
],
"query": "eurorack power",
"totalResults": 1
}
Empty Query (200)
When q is empty or missing:
{
"results": [],
"query": "",
"totalResults": 0
}
Response Fields
Result Object
| Field | Type | Description |
|---|---|---|
id | string | Unique document ID ({contentType}/{fileSlug}) |
slug | string | URL path to the content page |
title | string | Article title (plain text) |
description | string | Article description (plain text) |
tags | string[] | Article tags as array |
createdAt | string | Creation date |
excerpt | string | Truncated plain text excerpt centered around first match |
highlighted_title | string | Title with <mark> tags around matching terms |
highlighted_excerpt | string | Excerpt with <mark> tags around matching terms |
score | number | MiniSearch relevance score (higher = more relevant) |
Top-level Fields
| Field | Type | Description |
|---|---|---|
results | array | Array of result objects, sorted by relevance score |
query | string | The normalized search query |
totalResults | number | Total number of matching results |
Search Behavior
- Prefix matching: Partial words match (e.g.,
euromatcheseurorack) - Fuzzy matching: Tolerates minor typos (edit distance 0.2)
- Field boosting: Title matches rank higher than description, which ranks higher than tags and excerpt
- Highlighting: Matching terms are wrapped in
<mark>tags. Other HTML characters are escaped
Keyword Logging
Every search query (including zero-result searches) is logged to Netlify Blobs store keyword-logs for analytics. This is fire-and-forget and does not affect the response.
Logged data:
| Field | Description |
|---|---|
keyword | The search query |
timestamp | ISO 8601 timestamp |
resultCount | Number of results returned |
Development
In development mode (NODE_ENV === 'development'), the search runs entirely client-side using MiniSearch in the browser. The Netlify Function is not called. See the Search overview for details.
Implementation
- Function:
netlify/functions/search.ts - Search data:
netlify/functions/search-data.json(generated at build time byscripts/generate-search-index.mjs) - Search library: MiniSearch