Skip to main content
@craft-ts
LearnLearn with EffectAI agentsGuideReference
@craft-ts/core@craft-ts/component@craft-ts/dev-tools@craft-ts/effect@craft-ts/style@craft-ts/style-testing@craft-ts/i18n@craft-ts/i18n-effect@craft-ts/mcp
ExamplesMigrationEffect compatibility & maturityAdopting CraftTS progressivelyPress kitRoadmapBacklog
Beta
  • Learn
  • Learn with Effect
  • AI agents
  • Guide
  • Reference
  • Packages
  • Resources

i.Learn with Effect

  • Effect users: start here
  • Overview
  • 1. Start with a Craft component
  • 2. Derive UI state
  • 3. Put the domain in Effect
  • 4. Load data with Effect
  • 5. Write data with Effect
  • 6. Provide Layers and route the app
  • 7. Build forms and validate boundaries
  • 8. Test the graph
  • 9. Server functions — POC
  1. Learn with Effect
  2. 9. Server functions — POC

9. Call server functions — proof of concept#

Not a final contract

The server-function integration is currently a proof of concept. The file conventions, transport, middleware composition and production integration are not definitive yet and may change. Use this chapter to understand the current experiment and to build demos; do not treat it as a stable deployment API.

For the recommended public/protected access path, start with the server functions guide. This chapter keeps the lower-level POC details; the guide highlights the existing middleware, clientContext, session verification and refusal tests.

Goal: understand the current client → registry → Effect server path.

The current shape#

An exposed function has a server implementation and a client facade:

users/list.fn-client.ts
users/list.fn-serveur.ts
        │
        └── HTTP/RPC → createServer registry → Effect handler

The client imports only the server function's type. It must not import the server implementation at runtime.

Define the server implementation#

The current experimental API takes an identifier, an input schema and an exposure mode. The handler returns an Effect:

typescript
// users/list.fn-serveur.ts
import { serverFunction } from '@craft-ts/core';
import { Effect, Schema } from 'effect';

const inputSchema = Schema.toStandardSchemaV1(
  Schema.Struct({ filter: Schema.String }),
);

export const listUsers = serverFunction('demo.users.list', inputSchema, {
  exposure: 'client',
}).handler(({ input }) =>
  Effect.gen(function* () {
    const repository = yield* UserRepository;
    return yield* repository.list(input.filter);
  }),
);

The server handler's success, typed errors and Effect requirements are the source of truth. Do not duplicate a result type or an error list manually.

Define the client facade#

typescript
// users/list.fn-client.ts
import { createServerFunctionClient } from '@craft-ts/core';
import type { listUsers as ServerListUsers } from './list.fn-serveur';

export const getUsers =
  createServerFunctionClient<typeof ServerListUsers>('demo.users.list');

The component uses the facade like a typed function. Wrap it in queryEffect or mutationEffect if the call belongs to a resource lifecycle:

typescript
import { isCraftException } from '@craft-ts/core';

const users =
  yield *
  queryEffect('users', {
    params: () => ({ filter: search() }),
    loader: ({ params }) =>
      Effect.gen(function* () {
        const result = yield* Effect.promise(() => getUsers(params));
        if (isCraftException(result)) return yield* Effect.fail(result);
        return result;
      }),
  });

The exact transport adapter is still experimental. The repository's demo-with-server-function app currently uses createServer, executeEffect and a local HTTP bridge.

Transport failures are typed#

The client transport always converts failures that prevent a usable server function response into a CraftException with _tag: 'HttpError' and scope: 'ServerFunctionClient'. This includes a lost connection, an aborted request, a missing fetch implementation, a rejected custom transport and an unreadable response body. As with CraftHttpClient, a network failure has payload.status === 0; the original failure is kept in payload.body.

Server-side business failures remain their declared tags, so connection loss and domain errors can be handled separately by the same resource or mutation.

Register and execute on the server#

typescript
const application = createServer({
  functions: [listUsers],
  execute: executeEffect(runtimeLayer).run,
});

The runtime Layer supplies server-only services such as a repository or the current user. Never import secrets, credentials or server implementations into a client module.

Middleware and security#

The current demo also shows Effect middleware:

typescript
const audited = effectServerMiddleware('demo.audit', ({ next }) =>
  Effect.gen(function* () {
    yield* Effect.log('before');
    const result = yield* Effect.exit(next());
    yield* Effect.log('after');
    return yield* result;
  }),
);

Middleware may add typed failures, resolve Effect services and run before/after hooks. Client claims remain untrusted; authenticate and authorize again on the server, then publish only verified values to the handler context.

Current limitations#

Treat these as constraints of the POC, not promises of the final design:

  • the browser transport and development plugin are local experimental adapters;

  • client/server file boundaries are checked by the current architecture graph, but deployment integration is still evolving;

  • middleware APIs and the server registry may be renamed or reshaped;

  • the server must re-check authorization even if the client has a matching Effect Layer.

See the running examples in apps/demo-with-server-function and the server-function architecture plan in the repository when this work is promoted out of the prototype area.

What you gained#

You can experiment with typed Effect server calls while keeping an explicit client/server boundary. Keep this chapter isolated from stable application contracts until the POC is replaced by a final server-function API.

← 8. Test the graph

Back to the overview →

←8. Test the graph

On this page

  • The current shape
  • Define the server implementation
  • Define the client facade
  • Transport failures are typed
  • Register and execute on the server
  • Middleware and security
  • Current limitations
  • What you gained

@craft-ts · MIT licensed

GitHubnpm

Search