# Storefront menus API in Go (GoCommerce module)

> Keep a GoCommerce storefront’s menus as trees of links by handle, saved whole, resolved to storefront paths, and moved in and out as a CSV file.

- Canonical: https://kitcommerce.store/integrations/navigation/
- Last updated: 2026-10-30

---

Platform

## Storefront menus API in Go

A Go package that keeps the storefront’s menus: a header, a footer, a sidebar, each a tree of items an operator arranges by hand. A storefront reads a menu at /x/navigation/menus/{handle} with every item already resolved to a path, so it renders the tree without knowing what each item points at.

- **Platform** module
- **4** settings
- **7** tests
- **10** API operations
- **ext/navigation**

### What it does

In the package’s own words, it is “the storefront’s menus: a header, a footer, a sidebar, each a tree of items an operator arranges by hand.” An item points at a product, a collection, a category, a content page, the home page, or any URL.

An item names what it points at by handle rather than by id, so a menu survives a re-import and reads sensibly in an export. The public route turns each handle into a storefront path with the four path patterns on the Plugins screen, and the item’s kind travels with it for a storefront that wants to know.

A menu is saved whole, because that is how it is edited: dragged into shape and saved, not one item at a time. Every store starts with a header and a footer, so a storefront can ask for them by handle before anyone has opened the Menus screen.

### Configuration

Nothing has to be set. The four paths say where each kind of page lives on the storefront; set them on the Plugins screen, which is the setting that takes effect.

*Menus module settings — 4 settings, 0 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `ProductPath` Product page path | — | No | The product page’s path with {slug} in it, /products/{slug} by default. |
| `CollectionPath` Collection page path | — | No | The collection page’s path, /collections/{slug} by default. |
| `CategoryPath` Category page path | — | No | The category page’s path, /categories/{slug} by default. |
| `PagePath` Content page path | — | No | The content page’s path, /pages/{slug} by default. Each panel field has a default of its own, which stands in front of Config, so a path set only in Config is never read; see the limits. |

The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own `main()` you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.

### Setting it up

1. **Install the module** — Pass navigation.New to gocommerce.New, as below, or start the reference binary with -menus. It adds two tables and gives each store a header and a footer menu.
2. **Check the paths** — If the storefront’s product, collection, category or page addresses are not the defaults, set their patterns on the Plugins screen under Menus.
3. **Arrange a menu** — On Content › Menus, add items, drag them into shape, up to four levels, and save. The save is one PUT /api/admin/x/navigation/menus/{id}/items with the whole tree.
4. **Read it on the storefront** — GET /x/navigation/menus/header answers the menu with its items nested and each one’s url resolved. GET /x/navigation/menus lists every menu’s handle and title.

main.go

```
import (
	"github.com/itswadesh/gocommerce/core"
	"github.com/itswadesh/gocommerce/ext/navigation"
)

app, err := gocommerce.New(cfg,
	// The four paths are set on the Plugins screen.
	navigation.New(navigation.Config{}),
)
```

The package doc has no example of its own; this is how the reference binary’s -menus flag installs it. cfg is your gocommerce.Config. Import path `github.com/itswadesh/gocommerce/ext/navigation`.

### How it works

- **Six kinds of item**

  url, home, product, collection, category and page. Home resolves to /, a url is served as written, and the other four put the item’s handle into their path pattern in place of {slug}. Every item but Home needs a target.

- **A tree saved whole**

  Saving a menu deletes its items and writes the new tree in one transaction. Item ids are not kept: a tree dragged into a new shape is a new tree.

- **Header and footer from day one**

  Where a store has none, it gets a header (Home, Shop, Contact) and a footer (Search, Contact us, Privacy policy). A store opened later gets the same; a store that built its own keeps them.

- **Menus as a spreadsheet**

  GET /api/admin/x/navigation/export writes every menu as CSV: menu, menu_title, path, kind and target, with the trail of titles in one cell, such as Shop / Shirts. The file’s row order is the menu’s order.

- **An import that replaces only what it names**

  POST /api/admin/x/navigation/import rewrites each menu the file names and leaves the others alone. ?dry_run=1 rehearses and rolls back. A row it cannot place is skipped with its line number; files are capped at 8 MB. Export needs data.export, import data.import.

- **Rights of its own**

  menus.read for managers and staff, menus.write for managers, so a store can hand its navigation to somebody without handing over the products.

### What it does not do

Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.

- **Targets are not checked** — A handle is not looked up when a menu is saved or read. A link to a product that was renamed or deleted is served as it is, and leads to a page that is not there.
- **The switch does not hide menus** — The public routes do not check the plugin’s switch. Turning Menus off on the Plugins screen leaves /x/navigation/menus answering.
- **Config paths are ignored** — Each path field on the Plugins screen has a default, and the module reads the panel first, so a path set only in Config never takes effect. Set the paths on the Plugins screen.
- **Four levels deep** — A menu nests at most four levels. A deeper tree is refused on save, and a deeper row in a CSV file is skipped.
- **A title, a kind and a target** — That is all an item holds. There is no icon, picture or new-tab flag, and item titles are not translated.
- **Every menu is public** — GET /x/navigation/menus lists every menu’s handle and title to anyone, so a menu kept for later is visible by its handle.

FAQ

### Questions about the Menus module

**What does the storefront read?**

GET /x/navigation/menus/{handle}, such as header or footer, on the host that serves your GoCommerce API. It answers the menu with its items nested, each with its title, kind, target and resolved url, and an empty children list on a leaf.

**Why point at a handle and not an id?**

So a menu survives a re-import of the catalogue and reads sensibly in an export. The handle is the product’s, collection’s, category’s or page’s slug, put into the matching path pattern.

**Can I edit menus in a spreadsheet?**

Yes. Export them as CSV, edit the rows, and import the file back. Each menu the file names is replaced whole; a menu it does not name is not touched. Run it with ?dry_run=1 first to see what it would do.

**How deep can a menu go?**

Four levels, which in the package’s words is what a header bar can render without becoming a filesystem browser.

### Related modules

Each is a directory under `ext/`, imported the same way. Where this one fits in the store: [How modules extend GoCommerce](https://kitcommerce.store/features/development/).

- [Multi-language product catalogue in Go Serve GoCommerce product titles, descriptions and SEO fields in the shopper’s language: the translations module, the store’s languages, what stays untranslated.](https://kitcommerce.store/integrations/translations/)
- [Signed ecommerce webhooks in Go Send a GoCommerce store’s events to another server: register an https URL and the events it wants, and each is POSTed signed, retried with backoff and logged.](https://kitcommerce.store/integrations/webhooks/)
- [AI gateway in Go One place a GoCommerce store calls a model: the install’s Anthropic key paid from AI credit, or the store’s own key and cap, with data-sharing switches.](https://kitcommerce.store/integrations/ai-gateway/)
- [Storefront banners in Go Keep a GoCommerce storefront’s home page pictures as rows: slides across the top, grids of tiles under a heading, a phone picture, a switch and one saved order.](https://kitcommerce.store/integrations/banners/)

### Source

Everything on this page is read from [`ext/navigation`](https://github.com/itswadesh/gocommerce/tree/main/ext/navigation) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.

- [ext/navigation on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/navigation)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

### Try it against a store of your own

The backend + admin quick start runs GoCommerce’s API and admin on your own machine, without a storefront. Add this module to it and try it on test orders before a real one depends on it.

[Backend + admin quick start](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/navigation) · [All integrations](https://kitcommerce.store/integrations/)
