---
title: "@fbritoferreira/strapi: TypeScript Client for Strapi v5"
description: "Open-source TypeScript client for Strapi CMS v5 with full type safety, CRUD + upsert operations, advanced filtering, i18n support, and minimal dependencies. Built to eliminate boilerplate in content-driven apps."
author: "Filipe Brito Ferreira (Senior Front-End Platform Engineer)"
canonical: https://www.fbritoferreira.com/blog/lightweight-typescript-client-strapi-cms-v5-with-upsert/
published: 2025-11-17
tags: ["typescript", "strapi", "open-source", "developer-tools", "headless-cms"]
image: https://cdn.fbritoferreira.com/assets/blog/strapi-cover-1200.webp
---

I just released [@fbritoferreira/strapi](https://github.com/fbritoferreira/strapi), an open-source TypeScript client for Strapi CMS v5. It’s lightweight, fully typed, and built to make working with Strapi APIs less painful.

## **Why I Created This Package**

While working extensively with Strapi in TypeScript environments, I noticed how repetitive and error-prone it was to handle API requests, particularly when dealing with complex filters, populating relations, pagination, localization (i18n), and error handling. Existing clients often lacked full TypeScript support or introduced unnecessary dependencies, which didn't fit well with lightweight projects.

This led me to build a minimal, modern client that uses native ES modules and Fetch API, prioritizing strong typing and ease of use. One key enhancement I contributed is the upsert functionality, an atomic create-or-update operation based on query filters. This feature simplifies synchronizing content: it automatically creates an entry if it doesn’t exist or updates it if it does, without extra network calls or complex client-side logic.

## **Key Features**

- Full TypeScript support: Define entity types for accurate request/response typing.
- CRUD operations: Standardized methods like findMany, find, create, update, and delete.
- Upsert operations: Simplify data synchronization with atomic create-or-update based on filters.
- Advanced querying: Supports Strapi filters, population, pagination, and locale selection.
- Internationalization support: Automated handling and linking of localized data.
- Minimal dependencies: Only uses a small query-string serializer alongside native APIs.
- Clear error handling: Async operations return [error, data] tuples for simpler coding.

## **Example Usage**

Install the client easily:

```bash
npm install @fbritoferreira/strapi
```

Use it in your project:

```typescript
import { StrapiClient } from "@fbritoferreira/strapi";

interface Article {
  id: number;
  title: string;
  content: string;
}

const client = new StrapiClient<Article>(
  "https://your-strapi-instance.com",
  "your-jwt-token",
  "articles",
);

async function fetchArticles() {
  const [error, articles] = await client.findMany({ populate: ["*"] });

  if (!error && articles) {
    console.log("Fetched articles:", articles);
  }
}

// Upsert an article (create if not exists, update if exists)
async function upsertArticle() {
  const payload = { data: { title: "Hello World", content: "This is a test" } };

  const filters = { title: { $eq: "Hello World" } };

  const [err, article] = await client.upsert({ payload, filters });

  if (!err && article) {
    console.log("Upsert successful:", article);
  }
}
```

### **Contribute**

It’s open-source and I’m actively maintaining it. PRs, issues, and feature requests are all welcome.

[https://github.com/fbritoferreira/strapi](https://github.com/fbritoferreira/strapi)
