TL;DR: Beyond the structural mechanics, Lego and web development share a deep philosophy when it comes to design, problem-solving and optimizing. Before we dive into the technical details, let's talk about Lego.
- A part is the smallest unit in the Lego ecosystem.
- Any parts can be collected to form a set.
- Specific parts group together to form a minifig.
You can imagine the parts as the food materials in a restaurant. These materials can be collected to create meal sets(e.g. all day breakfast, noodle set, afternoon set...etc). A minifig is like a drink, which must be consisted of particular elements like water, tea spoon, ice..etc.
Lego keeps manufacturing these components, and it is the most successful toy company in the world by both sales and net profit now!

Relationships between sets, parts, and minifigs
About the Search
If you don't know much about Lego, start by searching things in your daily life: apple, car, house, coffee...
However, the features of the available Lego search engines are somewhat deficient. For example, the official Lego website only provides the search engine for sets. While Bricklink is a popular Lego sales platform and the search engine covers the needs of advanced Lego fans, its interface lacks of responsiveness and it could be overwhelming for starters.
Of course the showcase is not a full-featured one, but I attempt to enhance with some features like:
- If the search keywords contain typos, the search engine should suggest the correct spelling.
- To make all products get highlighted in turn, I use large cards in the masonry layout to catch the user's attention.
- Cache control should be applied whenever needed. The Lego data doesn't change frequently. For example, Lego produces 600 sets and introduces 1500 new parts on average every year. And the set pictures even won't change after produced. Successive searches will strain server resources if every request triggers a fresh data fetch. Caching for 24 hours (or even longer) is a reasonable approach to overcome this issue.
headers: {
'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate=43200'
}
AI Mode
This project uses a lightweight LLM as an intent parser alongside with SQL queries and dynamic vector retrieval.
I choose a lightweight Hugging Face Inference API model as a router. Through customized prompt engineering, the LLM is instructed to output user's intent (e.g. find a set produced in a particular year, or find a part with specific color, or abstract, contextual search). While Text-to-SQL would be a great approach with the well-normalized Lego data, this LLM routing approach excels in low latency and token usage is minimal. The system prompt would look like this:
const systemPrompt = `You are a LEGO search engine query parser. Analyze the user's search query and convert it into a JSON object.
Intent Rules:
1. "SQL_PART": Use this when asking for specific parts, pieces, or colors inside a set.
2. "SQL_SET": Use this when searching for LEGO sets by number, exact title, release year, year range, or theme (e.g., "sets between 2023 and 2025", "sets from 2020 to 2022", "sets in 2022").
3. "VECTOR": Use this ONLY for broad, semantic, conceptual, or thematic searches (e.g., "large medieval castles", "space ships").
Extract entities if present:
- set_num: Extracted set number (string).
- start_year: Extracted start year as integer for range queries (e.g., 2023 for "2023 to 2025", or same as year if exact year like "in 2022").
- end_year: Extracted end year as integer for range queries (e.g., 2025 for "2023 to 2025").
- year_operator: Comparison operator if SINGLE year mentioned without range:
* "before" / "earlier than" -> "<="
* "after" / "later than" -> ">="
* "in" / "from" / exact year -> "="
- theme: Extracted theme or topic keyword (e.g., "movie", "space").
- color: Extracted color.
- part_name: Extracted part name.
Respond ONLY with valid JSON in this exact structure:
{
"intent": "VECTOR" | "SQL_SET" | "SQL_PART",
"set_num": string | null,
"start_year": number | null,
"end_year": number | null,
"year_operator": "<=" | ">=" | "=" | null,
"theme": string | null,
"color": string | null,
"part_name": string | null
};`}For more abstract, contextual prompts (e.g house by the sea or cars for kids), the engine activates RAG (Retrieval-Augmented Generation) approach. It generates text embeddings and performs semantic similarity searches using pgvector.
Text embeddings stored in PostgreSQL using pgvector in the Lego sets table
As a Lego fan, I find it a good use case to search something like red parts in 10222, in which 10222 is the set number. All Lego set numbers are just digits, so the system can accurately identify that the user intents to find Lego pieces of specific color in the designated set. The following image is the assembled Lego Christmas Post Office (set number 10222). To quickly find all the red parts, just enter the prompt and you can see what the post office van is made of much more efficiently!
The assembled Lego Christmas Post Office (10222)
Another interesting point to note is, there are over 200 colors in the Lego system. Green is different from Dark green. For me, whether to display both green and dark green if user enters green parts in 10222 is a matter of suitability, rather than correctness. That said, another case needs accuracy; dark red. for example, there is no dark red parts in this set, so entering dark red parts in 10222 should not return results with red parts.

A part in dark green

A part in green
Notes:
- For showcase purpose, sets and parts can be found at this moment, it does not cater for individual minifigs yet.
- The Lego image data is extracted from the Lego community, some items do not have images of their corresponding colors. So you may see a white door picture but actually the part is red.
Working with Types
Typescript is a perfect use case for Lego data. There are actually other entities in the Lego ecosystem like colors, themes, inventories. But for simplicity, I just illustrate the use of Typescript to type all search results returned from the api:
interface SearchResultParts {
part_num: string;
name: string;
part_img_url: string;
}
interface SearchResultSets {
set_num: string;
name: string;
theme_id: number;
year: number;
set_img_url: string;
}
interface SearchResultMinifigs {
set_num: string;
name: string;
set_img_url: string;
}Final thoughts
This showcase demonstrates my technical competence as a full-stack specialist specifically in web development. But Lego is not just related to database, The Lego Powered Up parts even further seamlessly integrates STEM and there are a lot of IoT libraries that provide Python/Node.js wrappers bridging the codes with the physical Lego components. For example, you can build a web app to control the speed and direction of Lego trains with physical motor components!
Last but not least, see the About section to learn more about me and let's connect!