# Building JavaScript functions

> Anatomy of a JavaScript function, covering the HTTP endpoint and job shapes, typed contracts, response helpers, error handling, and logging.

Every function is a module that default-exports a `defineFunction(...)` object. The CLI discovers it through the `functions` map in `uipath.json`.

```typescript

export default defineFunction({
  name: "process-order",
  input: defineSchema<Input>(),
  output: defineSchema<Output>(),
  handler: async (input, ctx) => { /* ... */ },
});
```

## The two shapes

A single field pair decides how a function is invoked.

| Shape | Declaration | Invoked by |
|---|---|---|
| **HTTP endpoint** | `method` and `path` are set | An app or any HTTP client, through the function's trigger URL |
| **Job** | `method` and `path` omitted | A Maestro Service Task, a Run Job activity, the Orchestrator API, or a job trigger |

Both shapes are packaged and deployed the same way. An HTTP function is what backs a Coded App; a job function is a step inside a larger automation. See [HTTP triggers and routing](javascript-http-triggers.md) for the first and [Invoking functions](invoking-functions.md) for the second.

## Typed contracts

`defineSchema<T>()` turns a TypeScript interface into the JSON Schema that drives variable binding on every invocation surface. The interface is the single source of truth — it types the handler and declares the contract:

```typescript
interface CreateOrderInput {
  /** Customer reference. */
  customerId: string;
  /** @default 1 */
  quantity?: number;
}
```

Optional properties become optional in the schema, and a JSDoc `@default` tag carries the default value through. In JavaScript projects, pass a JSON Schema object literal in place of `defineSchema<T>()`.

Schemas are extracted from the source without running it, so write them as literals. A value referenced through a variable — a numeric bound, or a shared schema object — can be dropped from the extracted schema, leaving the function with no contract to bind against.

Declaring `output` is optional. When present, the handler's return value is validated against it before it leaves the function.

## Returning results

Return a plain object to send `200` with that object as the body:

```typescript
handler: async (input) => ({ orderId: input.customerId, processed: true }),
```

For anything else, return a response object or use a helper:

```typescript

return created({ id: "new-id" });          // 201
return notFound("No such order");          // 404
return { status: 202, body: { queued: true } };
```

:::warning
Because `{ status, body }` is the response shape, an output field of your own named `status` holding a number is read as a status code. The declared status is sent with an empty body and the rest of your payload is dropped, with no error. Name the field something else, such as `httpStatus`.
:::

## Errors

Throw `FunctionError` to fail with a specific status and message:

```typescript

if (!input.customerId) {
  throw new FunctionError("customerId is required", 400);
}
```

An uncaught error becomes a `500`. When the function runs as a job, a thrown error faults the job and the message reaches the job result.

## Logging

Use the SDK logger so output is attributed to the run and reaches Orchestrator job logs:

```typescript

logger.info(`Processed order ${input.orderId}`);
```

`console.*` output is not forwarded. Secret values must never be logged.

## Next steps

- [HTTP triggers and routing](javascript-http-triggers.md)
- [Function context](javascript-function-context.md) — identity, platform coordinates, and request data.
- [`defineFunction` reference](https://uipath.github.io/uipath-typescript/js-functions/api/define-function/) — every field and its defaults.
