# Connect a JavaScript website

> **Related:** [Project access](https://docs.atmyapp.com/account.md) · [Hand the website over to your client](https://docs.atmyapp.com/ai-assistant.md) · [Changelog](https://docs.atmyapp.com/changelog.md) · [Deploy your website](https://docs.atmyapp.com/deployment.md) · [Examples](https://docs.atmyapp.com/examples.md) · [Choose your integration](https://docs.atmyapp.com/integrations.md) · [Troubleshooting](https://docs.atmyapp.com/troubleshooting.md)
> **Also:** [HTML version](https://docs.atmyapp.com/quick-start) · [Docs index](https://docs.atmyapp.com/llms.txt)

---
Use `@atmyapp/core` in any JavaScript or TypeScript project. Your framework owns the HTML; AtMyApp supplies the editable content. If you use Astro, follow [the Astro setup](/guides/astro/) for automatic configuration, metadata, previews, and forms.

## 1. Prepare your project

Create a project in AtMyApp and configure its content source in Project Setup. Copy the project API URL and a public website API key. You also need a CLI token to upload your schema.

These credentials have different roles:

| Value                  | Used for                                   | Where it belongs                                         |
| ---------------------- | ------------------------------------------ | -------------------------------------------------------- |
| Project API URL        | Identifies the project and environment     | Website configuration; include the complete project path |
| Public website API key | SDK reads and permitted website operations | Server, build process, or browser                        |
| CLI token              | Project administration and schema uploads  | Local CLI session or protected CI configuration          |
| Preview key            | Access to a particular unpublished preview | That preview request; never a shared production default  |

The website API key is public by design. A CLI token is not a replacement for it. Use the URLs provided by your environment rather than guessing the hostname.

## 2. Install the packages

In your existing website project:

```sh
npm install @atmyapp/core @atmyapp/structure
npm install --save-dev @atmyapp/cli
```

Use a module-aware bundler for browser code. A bare import such as `@atmyapp/core` is resolved by your build tool, not by a plain browser script tag. This guide does not require React, Vue, or another UI framework.

## 3. Connect the CLI

```sh
npx atmyapp use --token cli_YOUR_TOKEN --url YOUR_PROJECT_API_URL
```

Replace both placeholders. Run this in the website directory. The session is stored in `.ama/session.json`; add that file to `.gitignore`. See [CLI workflow](/guides/cli/) for initialization and upload commands.

## 4. Define the editable content

Create `atmyapp.schema.ts` in the project root:

```ts
import { defineSchema, defineDocument, s } from "@atmyapp/structure";

export default defineSchema({
  definitions: {
    home: defineDocument({
      path: "home.json",
      description: "Text shown at the top of the homepage.",
      fields: {
        headline: s.string({
          min: 1,
          max: 100,
          description: "The main heading.",
        }),
        introduction: s.string({ format: "long", optional: true }),
      },
    }),
  },
});
```

The key `home` identifies the definition. `home.json` is its content path. Neither is the URL of your homepage. Choose stable identifiers; changes to these names can affect existing content and code.

## 5. Upload the schema and create content

```sh
npx atmyapp migrate --dry-run --verbose
npx atmyapp migrate
```

The first command lets you inspect the generated structure. The second uploads it to the connected project. A schema describes data; it does not populate every document with real website copy. Create the homepage content in AtMyApp and publish it before testing a normal production read.

Use this content shape:

```json
{
  "headline": "A website your team can update",
  "introduction": "Edit the content while keeping your custom design."
}
```

For existing content files, see [CLI uploads](/guides/cli/#file-commands). Keep your schema in source control alongside the code that renders it.

## 6. Create the runtime client

```ts
// src/atmyapp.ts (schema lives in the project root)
import { createAtMyAppClient } from "@atmyapp/core";
import schema from "../atmyapp.schema";

export const ama = createAtMyAppClient({
  baseUrl: "YOUR_PROJECT_API_URL",
  apiKey: "YOUR_PUBLIC_WEBSITE_API_KEY",
  schema,
  errorPolicy: "throw",
});
```

Replace the placeholders with your project values. Passing the actual schema enables typed definition names and returned content. Importing types alone does not enable runtime schema-aware helpers.

Core does **not** automatically read environment variables. Map your framework's configuration into these options. For Node, use `process.env`; for Vite browser code, use its public `import.meta.env` configuration. Astro's integration handles environment lookup for you.

## 7. Render your first document

For a browser page, add a heading and a status message:

```html
<h1 id="headline">Welcome</h1>
<p id="introduction"></p>
<p id="content-status" role="status"></p>
```

Then load this code through your site's bundler:

```ts
import { ama } from "./atmyapp";

async function renderHome() {
  const status = document.querySelector("#content-status")!;
  try {
    const home = await ama.storage.getValue("home");
    document.querySelector("#headline")!.textContent = home.headline;
    document.querySelector("#introduction")!.textContent =
      home.introduction ?? "";
    status.textContent = "";
  } catch {
    status.textContent =
      "Content is temporarily unavailable. Please try again.";
  }
}

void renderHome();
```

`getValue()` returns the document fields directly. `get()` returns a wrapper; `getFromPath()` reads a raw path without an inferred shape. Use `textContent` for plain text. Markdown and MDX require a renderer and an explicit sanitization/component policy in your application.

## 8. Choose when content is loaded

| Approach          | Where the SDK runs       | When published edits appear               |
| ----------------- | ------------------------ | ----------------------------------------- |
| Browser rendering | In the visitor's browser | On a new read, subject to caching         |
| Server rendering  | In your request handler  | On a new request, subject to your caches  |
| Static generation | During your build        | After a successful rebuild and deployment |

For static generation, await `ama.storage.getValue('home')` inside your framework's build loader and pass the returned fields into its template. Keep `errorPolicy: 'throw'` so a failed read can fail the build instead of producing empty pages. Configure a rebuild workflow; changing content cannot rewrite already generated HTML.

For server rendering, create preview-aware clients inside each request. Never put a preview key into a process-global client:

```ts
import { createAtMyAppClient } from "@atmyapp/core";
import schema from "../atmyapp.schema";

export function clientForRequest(request: Request) {
  const previewKey =
    new URL(request.url).searchParams.get("amaPreviewKey") || undefined;
  return createAtMyAppClient({
    apiKey: "YOUR_PUBLIC_WEBSITE_API_KEY",
    baseUrl: "YOUR_PROJECT_API_URL",
    schema,
    previewKey,
    mode: previewKey ? "priority" : "client",
    errorPolicy: "throw",
  });
}
```

Only enable preview handling on routes intended to render previews. Do not cache preview HTML publicly or silently turn a failed preview into published content. A static production page does not become a live preview merely because a query parameter was added. See [Astro previews](/guides/astro-previews/) for the integrated implementation.

## 9. Verify and hand over

Read a known published document, change it in AtMyApp, and verify the update through your chosen rendering path. Test unavailable content, an invalid API key, and an expired preview. Add [optional diagnostics](/guides/diagnostics/) when you want an inspectable report.

Continue with [schema design](/guides/structure/), [forms](/guides/forms/), and the [client handoff](/ai-assistant/).