# Setup options

dcupl offers three setups: **Console + Loader** (recommended), the **Loader with your own files**, and **SDK-only**. All of them use the same query API—your application code stays the same regardless of which you choose.

## <a id="console-loader"></a>Console + Loader (recommended)

The [Console](/console) manages your models and data sources visually. The **Loader** fetches everything at runtime from Console's CDN.

**Why it's recommended:**

- **Visual model editor** – Define schemas without writing JSON
- **External data sources** – Load data from any URL, or pull it in from APIs, S3, Azure or git with workflows
- **Environments** – Per-environment variables and headers (e.g. dev, staging, prod) in the loader configuration
- **Team collaboration** – Non-developers can edit models and data
- **Version control** – Draft → Production file versions for safe deployments

Where the project ID and the Browser key come from, and how to pin a version or pick an environment: [Load from the console](/sdk/guides/load-from-console).

## Loader with your own files

The Loader doesn't need the Console. Point it at any host that serves a `dcupl.lc.json` and the files it lists: your own server or CDN, or `dcupl serve` from the [CLI](/cli), which serves your project's `dcupl/` folder on `http://localhost:8083` by default.

Models are JSON files you host next to your data, and versioning is up to you (e.g. git).

**Use cases:**

- Local development against the files on your disk
- Projects that keep models and data in git and deploy them with the app
- Hosting the files yourself instead of on the dcupl CDN

## SDK-only

Define models and load data directly in code. No external dependencies.

**Use cases:**

- Quick prototypes and learning
- Small, static datasets bundled with your app
- Full programmatic control required

## Comparison

|                  | Console + Loader (recommended)  | Loader with your own files      | SDK-only               |
| ---------------- | ------------------------------- | ------------------------------- | ---------------------- |
| **Packages**     | `@dcupl/core` + `@dcupl/loader` | `@dcupl/core` + `@dcupl/loader` | `@dcupl/core`          |
| **Models**       | Visual editor in Console        | JSON files you host             | Defined in code        |
| **Data**         | Auto-fetched from URLs          | Auto-fetched from URLs          | Inline or manual fetch |
| **Environments** | Loader environments you define  | Loader environments you define  | Manual config          |
| **Versioning**   | Draft → Production versions     | Up to you (e.g. git)            | Up to you (e.g. git)   |

## Setup code

```typescript group="setup" tab="Console + Loader"
import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';

const dcupl = new Dcupl({
  config: {
    projectId: 'your-project-id', // From Console
    apiKey: 'your-api-key', // From Console
    version: 'draft', // CDN file version: 'draft' (default) or 'production'
  },
});

const loader = new DcuplAppLoader();
dcupl.loaders.add(loader);

// Fetch configuration and data from Console CDN
await loader.config.fetch();
await loader.process(); // loads every resource in the configuration
await dcupl.init();

// Query your data
const list = dcupl.lists.create({ modelKey: 'Product' });
const results = list.catalog.query.items();
```

```typescript group="setup" tab="Your own files"
import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';

const dcupl = new Dcupl();

const loader = new DcuplAppLoader();
dcupl.loaders.add(loader);

// Fetches `${baseUrl}/dcupl.lc.json`; `${baseUrl}` in resource URLs
// resolves to the same host
await loader.config.fetch({ baseUrl: 'http://localhost:8083' }); // dcupl serve, or your own host
await loader.process();
await dcupl.init();

// Query your data
const list = dcupl.lists.create({ modelKey: 'Product' });
const results = list.catalog.query.items();
```

```typescript group="setup" tab="SDK-Only"
import { Dcupl } from '@dcupl/core';

const dcupl = new Dcupl();

// Define model in code
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
  ],
});

// Load data inline
dcupl.data.set(
  [
    { key: 'p1', name: 'Widget', price: 29.99 },
    { key: 'p2', name: 'Gadget', price: 49.99 },
  ],
  { model: 'Product' },
);

await dcupl.init();

// Query your data
const list = dcupl.lists.create({ modelKey: 'Product' });
const results = list.catalog.query.items();
```

For every `new Dcupl()` option, see [DcuplInitOptions](/sdk/api/dcupl#dcuplinitoptions).

## Script tag (no build step)

For a page without a bundler, load the `@dcupl/bundle` browser build. It contains the SDK and the Loader and defines one global function, `initDcupl()`:

```html filename="index.html"
<script src="https://cdn.jsdelivr.net/npm/@dcupl/bundle/dist/dcupl.bundle.min.js"></script>
<script>
  initDcupl({
    options: { config: { projectId: 'your-project-id', apiKey: 'your-api-key' } },
  })
    .then(({ dcupl }) => {
      const list = dcupl.lists.create({ modelKey: 'Product' });
      console.log(list.catalog.query.items());
    })
    .catch((error) => console.error('dcupl failed to load', error));
</script>
```

- `initDcupl({ options, appLoaderConfig, processOptions })` runs the Console + Loader setup above: `new Dcupl(options)`, `loader.config.fetch(appLoaderConfig)`, `loader.process(processOptions)` and `dcupl.init()`. It resolves `{ dcupl, loader }` and also sets `window.dcupl`. It rejects when the loader configuration can't be fetched (`Invalid Loader Configuration`).
- For your own files, pass `appLoaderConfig: { baseUrl: 'http://localhost:8083' }` instead of the project config.
- It resolves even when `init()` resolves `false`; the cause is logged to the console (see [When init() Resolves false](/sdk/guides/error-handling#when-init-resolves-false)).
- The URL above loads the latest release. In production, pin a version and add its `integrity` hash together with `crossorigin="anonymous"`. Without `crossorigin`, the browser blocks a cross-origin script that has an `integrity` hash:

```html title="Pinned version"
<script
  src="https://cdn.jsdelivr.net/npm/@dcupl/bundle@<version>/dist/dcupl.bundle.min.js"
  integrity="sha384-<hash>"
  crossorigin="anonymous"
></script>
```

## Applications

A loader configuration can define **applications**: named presets in the `applications[]` array of `dcupl.lc.json` that pick resources by tag. Pass a key to `process({ applicationKey })` to load only that part of the project; [Load one application](/sdk/guides/load-from-console#load-one-application) shows the call and what an unknown key does, [Applications in the Loader API](/sdk/api/loader#applications) the full behaviour, and [Applications](/console/applications) how the Console uses them.

## Migrating from SDK-only to Console

1. Create a project in [Console](/console)
2. Define models in the visual editor (or import existing JSON)
3. Add data sources (URLs, file uploads)
4. Get your `projectId` and `apiKey`
5. Replace `dcupl.models.set()` and `dcupl.data.set()` with the Loader setup
6. **Query code stays the same**—no changes needed

## Next steps

- [Your first console project](/console/getting-started) — Set up your first project
- [Load from the console](/sdk/guides/load-from-console) — Project ID, Browser key, version and environments
- [SDK Quick Start](/sdk/quick-start) — Learn the query API
- [Core concepts](/sdk/core-concepts) — Deeper dive into models and lists
