---
url: /sidecar/development.md
description: >-
  Running a sidecar under kosmo serve - how the reload works, why the close
  function is what frees the port, and when not to set serve at all.
---

In dev mode, `kosmo serve` can run a sidecar alongside your app, so a worker is up while you work on the routes that feed it.
Whether it does is the `serve` key.

Without `serve`, a sidecar is built and left alone - it is a build artifact like any other, and starting it is yours.
With `serve: true`, `kosmo serve` imports the entry into its own Vite environment and calls `start()`.

When the entry changes - or any file it imports - it:

1. re-imports the entry
2. calls `teardown()` on the running service
3. calls the close function that service's `start()` returned
4. calls `start()` on what it just imported

The import comes first so that a save that cannot compile changes nothing:
it throws at step 1, and the service keeps running.

It is one process, so there is no signal to catch and nothing to wait for on exit -
but that also means the close function is the only thing that frees a port.

Return one that actually closes the server,
or the next reload hits `EADDRINUSE` and keeps hitting it until you restart the dev server.

A reload that throws is reported, the dev server stays up, and the next good save retries.

::: warning Development only
`kosmo preview` and `dist/run.js` build sidecars but never start them.
[Details ›](/sidecar/production.md)
:::

***

### When not to serve

`serve` earns its keep when the service holds something open between edits -
a socket bound, a consumer subscribed, a pool connected.
That is what makes a restart worth doing, and the close function is what releases it.

A sidecar whose `start()` holds nothing has none of that.
There is no socket to rebind, nothing for the reload to free,
and no state that being up between saves preserves -
the dev server is only re-running a function that returns.

Leave `serve` off and let it be a build artifact:

```ts [src/mailer/kosmo.config.ts]
export default defineConfig({
  sidecar: {
    entry: "./entry.ts",
    run: "./run.ts",
    // nothing to keep alive between edits - build it, start it yourself
    serve: false,
  },
});
```

An empty close function is the tell.
If there is nothing to put in it, there is nothing `serve: true` is keeping alive for you.

***

### Typechecking

Nothing to configure. A sidecar lives inside a source folder,
and a folder's `tsconfig.json` already covers everything under it,
so `pnpm typecheck mailer` checks it like any other file in the folder.

A sidecar that wraps third-party JavaScript, or one you simply do not want checked,
opts out with the folder-level `typecheck` key:

```ts [kosmo.config.ts]
defineConfig({
  // ...
  sidecar: { entry: "./entry.mjs" },
  typecheck: false,
});
```
