> ## Documentation Index
> Fetch the complete documentation index at: https://docs.judt.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Test on the dev server

> Run and test an App in its sandbox before you publish it.

Each App's sandbox runs a dev server: `celld dev`, from celld, the Judt runtime, on port 3000. The dev server runs the same Worker code that production runs, with its own data. Use it to test each change before you deploy.

## Before you begin

To follow these steps, you need a way to run commands in the App's sandbox, such as the in-product agent, the API, or the MCP server. The sandbox starts when a request needs it.

The platform starts the dev server when the sandbox starts. The first start runs `npm ci` and the build, so it can take some seconds.

If the dev server stops, for example after a crash or a failed `npm ci` or `build` script, then nothing starts it again until the next preview request. Until then, `curl` to port 3000 gets "connection refused". If the start failed, then the preview request reports the failure and doesn't start the dev server again until a file changes.

To send a preview request, use the in-product agent's `preview` tool, the MCP server's `create_preview` tool, or `POST /api/apps/{id}/preview`. For more information about sandboxes and preview URLs, see [Sandbox](https://docs.judt.ai/manage/concepts/sandbox.md).

## How the dev server starts

When the dev server starts, it prepares the App the way that a deploy does, and then it serves the App. It does the following, in order:

1. If the `package-lock.json` file differs from the one that the last install used, then it runs `npm ci`. Install scripts don't run.
2. If the `package.json` file has a `build` script, then it runs the script one time.
3. If the `package.json` file has a `dev` script, then it starts the script and keeps it running beside the dev server. Use the `dev` script for a watcher that rebuilds on change, such as `tsc --watch`.
4. It starts `celld dev` on port 3000.

The sandbox doesn't keep `node_modules` when it stops, so the dev server installs the dependencies again after the sandbox starts. A change to the `package.json` file takes effect the next time that the dev server starts. You don't need to restart anything yourself. For more information about the `build` and `dev` scripts, see [npm packages and builds](https://docs.judt.ai/build/npm-and-builds.md).

## Test a change

**1. Check the syntax**

In the sandbox, check each module that you changed:

```bash theme={null}
node --check index.js
```

If the App has a `build` script, run the build instead. `node --check` checks only the syntax. Node.js can't run Worker code, so a module that passes the check can still fail on the dev server.

**2. Send a preview request**

Send a preview request before you send other requests. It starts the dev server if it isn't running, and it waits for the rebuild that your change started. If the build or the start failed, it returns `422 preview_failed` with the build's output. Without this step, `curl` can get an answer from the previous build.

**3. Send a request**

In the sandbox, request a route from the dev server:

```bash theme={null}
curl -s http://127.0.0.1:3000/
```

To open the App in a browser, use the URL that the preview request returned.

**4. Read the logs**

If a request fails, read the App's logs. The dev server's entries have the `development` environment. For more information, see [Troubleshoot](https://docs.judt.ai/manage/guides/troubleshoot.md).

## Logs and failed builds

The App's logs collect the following entries from the dev server:

* **Your code's output**: each `console.*` call and each uncaught exception, with the `app` source. Production writes the same entries.
* **The dev server's own events**: builds, reloads, and exits, with the `dev_server` source. A `dev.build_failed` entry includes the bundler's report.

If a build fails, then the previous build keeps serving, so a request can still get an answer. A preview request returns `422 preview_failed` until the App builds again. You can also check the logs for a `dev.build_failed` entry.

## Dev data

The dev server keeps its data in the `.celld/dev/` directory. The data stays after the dev server restarts. Dev data and production data are separate.

Commands can't read the `.celld/dev/` directory. To read dev data, send a request to the App.

## Generated dev server files

The dev server runs from `.judt-dev.json`, a copy of the `wrangler.json` file that the platform keeps. For an App with D1, its `main` key names `.judt-dev.js`, which loads your entry module and applies the migrations. Don't edit or delete these files. If a dev server error names `.judt-dev.json`, then correct your `wrangler.json` file.

## Test D1 migrations

The dev server applies D1 migrations to the dev database, as a deploy does to production. Each time a build starts to serve, the dev server applies the pending files in the `migrations/` directory. A new file applies after the rebuild that it starts. Don't write code that applies migrations.

If a migration fails, then the preview request fails with `preview_failed` and the SQL error, and nothing from that file stays. Correct the file, and the next rebuild applies it. A file that the dev server applied doesn't apply again when you change it, so add a new file. For more information, see [D1](https://docs.judt.ai/build/storage/d1.md).

## Test a Cron Trigger

The dev server runs Cron Triggers on their schedule, and you can't start one manually. To test the job, put it in a function, and call the function from a temporary route. Remove the route after the test:

```js index.js theme={null}
// TEMPORARY
if (url.pathname === "/__dev/cleanup") return json(await cleanup(env, Date.now()));
```

In this example, `cleanup` is the job function, and `json` is a helper that returns a JSON response.

For more information, see [Cron Triggers](https://docs.judt.ai/build/background/cron-triggers.md).

## Secrets on the dev server

Your code reads the App's Secrets as `env.NAME` on the dev server, as it does in production. For more information, see [Vars and Secrets](https://docs.judt.ai/build/vars-and-secrets.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.