ThinkNimble Research

tn-models
Active

tn-models

Frontend API model service builder

A library for creating API models that mirror a RESTful backend. Handles serialization, and data transformation with type safety.

Overview

tn-models is a TypeScript library that brings functional programming principles to API model management. It provides a type-safe, composable approach to handling data serialization, deserialization, and transformation between your frontend and backend.

Features

Installation

# npm
npm install @thinknimble/tn-models-fp

# yarn
yarn add @thinknimble/tn-models-fp

# pnpm
pnpm add @thinknimble/tn-models-fp

Core Concepts

Models as Functions

Models are defined as pure functions that transform and validate data:

import { model, field } from "@thinknimble/tn-models-fp";

const UserModel = model({
  id: field.number(),
  email: field.string().email(),
  name: field.string().optional(),
  createdAt: field.date(),
});

// Type is automatically inferred
type User = typeof UserModel._type;

Functional Pipelines

Chain operations using functional composition:

import { pipe } from "fp-ts/function";
import { map } from "fp-ts/Either";

const result = pipe(
  apiResponse,
  UserModel.decode,
  map((user) => ({
    ...user,
    displayName: user.name || user.email,
  }))
);

Error Handling

Errors are handled using the Either monad pattern:

const decoded = UserModel.decode(data);

if (isRight(decoded)) {
  // Success - decoded.right contains the valid user
  console.log(decoded.right);
} else {
  // Failure - decoded.left contains validation errors
  console.error(decoded.left);
}

API Integration

REST APIs

const api = createApi({
  baseURL: "https://api.example.com",
  models: {
    user: UserModel,
    post: PostModel,
  },
});

// Automatically typed responses
const user = await api.get("/users/1"); // Returns Either<Error, User>

GraphQL Integration

const query = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      email
      name
    }
  }
`;

const result = pipe(
  await client.query({ query, variables: { id: "1" } }),
  UserModel.decode
);

Advanced Features

Custom Transformers

Create custom field transformers for complex data types:

const MoneyField = field.custom({
  decode: (value) => Money.fromCents(value),
  encode: (money) => money.toCents(),
});

Nested Models

Support for complex nested structures:

const OrderModel = model({
  id: field.string(),
  items: field.array(OrderItemModel),
  customer: UserModel,
  total: MoneyField,
});

Use Cases

Philosophy

TN Models FP embraces functional programming principles to create more predictable, testable, and maintainable code. By treating models as pure functions and errors as values, it eliminates many common runtime errors and makes data flow explicit.

Contributing

We welcome contributions! Visit our GitHub repository for contribution guidelines and to browse open issues.