diff --git a/lefthook.yml b/lefthook.yml
new file mode 100644
index 00000000..f6e5dfe5
--- /dev/null
+++ b/lefthook.yml
@@ -0,0 +1,35 @@
+# EXAMPLE USAGE:
+#
+# Refer for explanation to following link:
+# https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
+#
+# pre-push:
+# commands:
+# packages-audit:
+# tags: frontend security
+# run: yarn audit
+# gems-audit:
+# tags: backend security
+# run: bundle audit
+#
+# pre-commit:
+# parallel: true
+# commands:
+# eslint:
+# glob: "*.{js,ts,jsx,tsx}"
+# run: yarn eslint {staged_files}
+# rubocop:
+# tags: backend style
+# glob: "*.rb"
+# exclude: '(^|/)(application|routes)\.rb$'
+# run: bundle exec rubocop --force-exclusion {all_files}
+# govet:
+# tags: backend style
+# files: git ls-files -m
+# glob: "*.go"
+# run: go vet {files}
+# scripts:
+# "hello.js":
+# runner: node
+# "any.go":
+# runner: go run
diff --git a/packages/restate-sdk-effect/.eslintignore b/packages/restate-sdk-effect/.eslintignore
new file mode 100644
index 00000000..bc70c0df
--- /dev/null
+++ b/packages/restate-sdk-effect/.eslintignore
@@ -0,0 +1,3 @@
+node_modules
+dist
+src/generated
diff --git a/packages/restate-sdk-effect/README.md b/packages/restate-sdk-effect/README.md
new file mode 100644
index 00000000..07f20645
--- /dev/null
+++ b/packages/restate-sdk-effect/README.md
@@ -0,0 +1,62 @@
+[](https://docs.restate.dev)
+[](https://github.com/restatedev/examples)
+[](https://www.npmjs.com/package/@restatedev/restate-sdk-effect)
+[](https://discord.gg/skW3AZ6uGd)
+[](https://twitter.com/intent/follow?screen_name=restatedev)
+
+# Restate Typescript SDK Effect integration
+
+[Restate](https://restate.dev/) is a system for easily building resilient applications using *distributed durable async/await*.
+
+This package contains a effect integration, allowing to define input/output models of your handlers.
+
+```typescript
+import * as restate from "@restatedev/restate-sdk";
+import { serde } from "@restatedev/restate-sdk-effect";
+import { Schema } from "effect";
+
+const Greeting = Schema.Struct({
+ name: Schema.String
+});
+
+const greeter = restate.service({
+ name: "greeter",
+ handlers: {
+ greet: restate.handlers.handler(
+ {
+ input: serde.effect(Greeting),
+ output: serde.effect(Schema.String),
+ },
+ async (ctx, greeting) => {
+ return `Hello ${greeting.name}!`;
+ }
+ ),
+ },
+});
+
+export type Greeter = typeof greeter;
+
+restate.serve({ services: [greeter], port: 9080 });
+```
+
+For the SDK main package, checkout [`@restatedev/restate-sdk`](../restate-sdk).
+
+## Community
+
+* 🤗️ [Join our online community](https://discord.gg/skW3AZ6uGd) for help, sharing feedback and talking to the community.
+* 📖 [Check out our documentation](https://docs.restate.dev) to get quickly started!
+* 📣 [Follow us on Twitter](https://twitter.com/restatedev) for staying up to date.
+* 🙋 [Create a GitHub issue](https://github.com/restatedev/sdk-typescript/issues) for requesting a new feature or reporting a problem.
+* 🏠 [Visit our GitHub org](https://github.com/restatedev) for exploring other repositories.
+
+## Using the library
+
+To use this library, add the dependency to your project:
+
+```shell
+npm install --save-dev @restatedev/restate-sdk-effect
+```
+
+## Versions
+
+This library follows [Semantic Versioning](https://semver.org/).
diff --git a/packages/restate-sdk-effect/api-extractor.json b/packages/restate-sdk-effect/api-extractor.json
new file mode 100644
index 00000000..a93ade2b
--- /dev/null
+++ b/packages/restate-sdk-effect/api-extractor.json
@@ -0,0 +1,7 @@
+/**
+ * Config file for API Extractor. For more info, please visit: https://api-extractor.com
+ */
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "extends": "../../api-extractor.base.json"
+}
diff --git a/packages/restate-sdk-effect/package.json b/packages/restate-sdk-effect/package.json
new file mode 100644
index 00000000..0b56e08e
--- /dev/null
+++ b/packages/restate-sdk-effect/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "@restatedev/restate-sdk-effect",
+ "version": "1.0.0",
+ "description": "Restate Typescript SDK Effect",
+ "author": "Alexander Teague",
+ "license": "MIT",
+ "email": "alex@teague.de",
+ "homepage": "https://github.com/restatedev/sdk-typescript#readme",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/restatedev/sdk-typescript.git"
+ },
+ "bugs": {
+ "url": "https://github.com/restatedev/sdk-typescript/issues"
+ },
+ "type": "module",
+ "sideEffects": false,
+ "main": "./dist/cjs/src/public_api.js",
+ "types": "./dist/cjs/src/public_api.d.ts",
+ "module": "./dist/esm/src/public_api.js",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/src/public_api.d.ts",
+ "default": "./dist/esm/src/public_api.js"
+ },
+ "require": {
+ "types": "./dist/cjs/src/public_api.d.ts",
+ "default": "./dist/cjs/src/public_api.js"
+ }
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "devDependencies": {
+ "@restatedev/restate-sdk-core": "^1.8.3"
+ },
+ "dependencies": {
+ "effect": "^3.17.9"
+ },
+ "scripts": {
+ "api:extract": "api-extractor run --local",
+ "build": "npm run build:cjs && npm run build:esm",
+ "build:cjs": "tsc --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'",
+ "build:esm": "tsc --outDir ./dist/esm --declaration --declarationDir ./dist/esm",
+ "lint": "eslint --ignore-path .eslintignore --max-warnings=0 --ext .ts .",
+ "format": "prettier --ignore-path .eslintignore --write \"**/*.+(js|ts|json)\"",
+ "format-check": "prettier --ignore-path .eslintignore --check \"**/*.+(js|ts|json)\"",
+ "attw": "attw --pack",
+ "verify": "npm run format-check && npm run lint && npm run build && npm run attw && npm run api:extract",
+ "release": "release-it"
+ },
+ "engines": {
+ "node": ">= 20.19"
+ },
+ "directories": {
+ "example": "examples",
+ "test": "test"
+ },
+ "publishConfig": {
+ "@restatedev:registry": "https://registry.npmjs.org"
+ }
+}
diff --git a/packages/restate-sdk-effect/src/public_api.ts b/packages/restate-sdk-effect/src/public_api.ts
new file mode 100644
index 00000000..6adbe503
--- /dev/null
+++ b/packages/restate-sdk-effect/src/public_api.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
+ *
+ * This file is part of the Restate SDK for Node.js/TypeScript,
+ * which is released under the MIT license.
+ *
+ * You can find a copy of the license in file LICENSE in the root
+ * directory of this repository or package, or at
+ * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
+ */
+
+export { serde } from "./serde_api.js";
+export type { Serde } from "./serde_api.js";
diff --git a/packages/restate-sdk-effect/src/serde_api.ts b/packages/restate-sdk-effect/src/serde_api.ts
new file mode 100644
index 00000000..1ad4fff5
--- /dev/null
+++ b/packages/restate-sdk-effect/src/serde_api.ts
@@ -0,0 +1,78 @@
+import type { Serde } from "@restatedev/restate-sdk-core";
+import { Effect, JSONSchema, ParseResult, Schema } from "effect";
+
+export type { Serde } from "@restatedev/restate-sdk-core";
+
+class EffectSerde implements Serde {
+ contentType? = "application/json";
+ jsonSchema?: object | undefined;
+
+ constructor(private readonly schema: Schema.Schema) {
+ // Generate JSON schema from Effect Schema
+ this.jsonSchema = JSONSchema.make(schema);
+
+ // Handle void/undefined types
+ if (schema.Type === Schema.Void || schema.Type === Schema.Undefined) {
+ this.contentType = undefined;
+ }
+ }
+
+ serialize(value: A): Uint8Array {
+ if (value === undefined) {
+ return new Uint8Array(0);
+ }
+
+ // Encode the value using the schema
+ const encoded = Schema.encode(this.schema)(value);
+
+ // Run the effect synchronously and handle potential errors
+ const result = Effect.runSync(
+ Effect.catchAll(encoded, (error) =>
+ Effect.die(
+ new Error(
+ `Serialization failed: ${ParseResult.TreeFormatter.formatErrorSync(
+ error
+ )}`
+ )
+ )
+ )
+ );
+
+ return new TextEncoder().encode(JSON.stringify(result));
+ }
+
+ deserialize(data: Uint8Array): A {
+ const js =
+ data.length === 0
+ ? undefined
+ : JSON.parse(new TextDecoder().decode(data));
+
+ // Decode the value using the schema
+ const decoded = Schema.decode(this.schema)(js);
+
+ // Run the effect and handle errors
+ return Effect.runSync(
+ Effect.catchAll(decoded, (error) =>
+ Effect.die(
+ new Error(
+ `Deserialization failed: ${ParseResult.TreeFormatter.formatErrorSync(
+ error
+ )}`
+ )
+ )
+ )
+ );
+ }
+}
+
+export namespace serde {
+ /**
+ * An Effect Schema based serde.
+ *
+ * @param schema the Effect schema
+ * @returns a serde that will validate the data with the Effect schema
+ */
+ export const effect = (schema: Schema.Schema): Serde => {
+ return new EffectSerde(schema);
+ };
+}
diff --git a/packages/restate-sdk-effect/tsconfig.eslint.json b/packages/restate-sdk-effect/tsconfig.eslint.json
new file mode 100644
index 00000000..c805a67c
--- /dev/null
+++ b/packages/restate-sdk-effect/tsconfig.eslint.json
@@ -0,0 +1,5 @@
+{
+ "extends": "./tsconfig.json",
+ "include": ["src/**/*.ts", "test/**/*.ts"],
+ "references": [{ "path": "../restate-sdk-core" }]
+}
diff --git a/packages/restate-sdk-effect/tsconfig.json b/packages/restate-sdk-effect/tsconfig.json
new file mode 100644
index 00000000..9e060ce4
--- /dev/null
+++ b/packages/restate-sdk-effect/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "./dist/esm",
+ "paths": {}
+ },
+ "include": ["src/**/*.ts"],
+ "references": [{ "path": "../restate-sdk-core" }]
+}