---
title: Read content from a site
description: Read what you publish from a Worker over a service binding, from a Nuxt, Astro, React or Vue site, or from anywhere over HTTP with a read token.
section: guides
order: 2
---

# Read content from a site

Your site reads published content only: a draft, or the unpublished changes of an entry, never reach it. There are three ways in.

## From a Worker, over a service binding

A Worker on the same Cloudflare account binds the `Hollware` entrypoint of your Hollware Worker. No network, no token:

```jsonc
// wrangler.jsonc of the site
"services": [
  { "binding": "HOLLWARE", "service": "my-cms", "entrypoint": "Hollware" }
]
```

```ts
const answer = await env.HOLLWARE.getEntry('my-site', 'blog', 'hello-world', 'en', {
  formats: ['html'],
})
if (answer.outcome === 'ok') return new Response(answer.entry.html)
```

The binding has six methods, all read-only: `getEntry`, `listEntries`, `getTranslations`, `describeSite`, `search` and `listRedirects`. `getEntry` returns the Markdown, the Comark document or the HTML, as `formats` asks; with `related: true` the published entries closest to it by meaning, and with `references: true` the entries its reference fields name (an author, a series: see [Contracts](/docs/contracts/)). Its images come with their `srcset` and size ([Images](/docs/images/)), and the fields the collection has Hollware fill (a date, the reading time) come filled in its frontmatter ([Configure your content](/docs/configure-your-content/)).

```ts
const found = await env.HOLLWARE.search('my-site', 'how do I cache pages', { locale: 'en', limit: 5 })
```

`search` finds published entries by their words, by meaning, or both (`mode: 'fulltext' | 'semantic' | 'hybrid'`, hybrid by default), and with `passages: true` the section of each that matched, for a model to answer with. Search by meaning needs Workers AI and Vectorize: see [Search and analytics](/docs/search-and-analytics/).

When an entry moves to another slug, its old path is kept as a redirect. Answer it with a 301:

```ts
const moved = await hollware.redirectFor('blog', 'en', slug) // createHollware() of any package
if (moved) return Response.redirect(new URL(moved.path ?? moved.slug, request.url), 301)
```

## From a framework

Each framework has a package that reads Hollware and renders what it reads with your own components, with previews of drafts and images ready:

| Framework | Package | Guide |
| --- | --- | --- |
| Nuxt | `@hollware/nuxt` | [Nuxt](/docs/nuxt/) |
| Astro | `@hollware/astro` | [Astro](/docs/astro/) |
| React Router, Next.js, TanStack Start | `@hollware/react` | [React](/docs/react/) |
| Vue 3 without Nuxt | `@hollware/vue` | [Vue](/docs/vue/) |

Any other JavaScript reads with `@hollware/client`, the package all of them build on: `createHollware` over a binding or a read token, and `imageOf` for the images of the frontmatter.

## Over HTTP, with a read token

The read API serves the same reads at `/v1`:

```sh
curl -H "Authorization: Bearer hw_read_..." "http://localhost:5199/v1/blog/en/hello-world?formats=html"
```

| Request                                         | Answer                         |
| ----------------------------------------------- | ------------------------------ |
| `GET /v1/site`                                  | the site: locales, collections |
| `GET /v1/search?q&collection&locale&limit&mode&passages&minScore` | the published entries that match q, best first |
| `GET /v1/{collection}?locale&tag&limit&cursor` | a page of published entries    |
| `GET /v1/{collection}/{locale}/{slug}?formats=&related=&references=` | one entry, with its related entries and the entries it names when asked |
| `GET /v1/{collection}/translations/{group}`     | the entries of a translation group |
| `GET /v1/redirects?collection&locale&limit&cursor` | the old paths of the entries that moved, and where they go |

Answers carry an ETag; send it back in `If-None-Match` to get a 304. The OpenAPI document is at `/v1/openapi.json`.

A **read token** (`hw_read_`) reads published content over this API and nothing else, so it can sit in a build, or even in a browser:

```sh
npx hollware token create --read --label my-site            # in the project, for the local database
npx hollware token create --read --label my-site --remote   # for the deployed one
```

Like every token, it is printed once: Hollware keeps only its hash. Locally the read API is at `http://localhost:5199/v1`; once deployed, at the public URL of your Hollware. `blog` is the starter's collection: use the names `describe_site` returns.

For a browser, list the origins of your pages in the admin, under **Site**, as *Origins that read from the browser*, such as `https://example.com`, or have an agent add them with `update_site`. They apply at once.
