Skip to content

Supporting multiple Lemmy API versions and other similar APIs

Christian :) edited this page Jun 4, 2025 · 2 revisions

This is a placeholder as I'm still working on support for multiple Lemmy versions.

But high level, the plan is:

const api = await apiClient({ instance: "https://lemmy.ml" })

// When we initialize apiClient, it will hit https://lemmy.ml/nodeinfo/2.1 and check which Lemmy version it's using

// Depending on the Lemmy version, 1 of 2 API wrappers will be returned, LemmyV3Api or LemmyV4Api

// LemmyV3Api and LemmyV4Api both implement abstract class ApiBlueprint

const post = api.getPost({ apId: "" })

// When I call the above function, it knows how to hit both Lemmy v3 or v4 Lemmy API endpoints depending
// on the API version hit https://lemmy.ml/nodeinfo/2.1 returned

// Instead of post being the raw data returned from the Lemmy API, we have normalized it by converting it
// into an internal representation of post that is identically regardless of the API version we are fetching from

// That means we can fetch from lemmy.ml v3, cache that data, fetch from lemmy.ml v4 the next day, and append to the cache
// without worrying about mixing data types since it's all been normalized

const postSchema = z.object({
  createdAt: z.string(),
  id: z.number(),
  apId: z.string(),
  communitySlug,
  communityApId: z.string(),
  creatorId: z.number(),
  creatorApId: z.string(),
  creatorSlug: z.string(),
  title: z.string(),
  body: z.string().nullable(),
  thumbnailUrl: z.string().nullable(),
  thumbnailAspectRatio: z.number().nullable(),
  downvotes: z.number(),
  upvotes: z.number(),
  commentsCount: z.number(),
  altText: z.string().optional(),
  url: z.string().nullable(),
  urlContentType: z.string().nullable(),
  removed: z.boolean(),
  optimisticRemoved: z.boolean().optional(),
  deleted: z.boolean(),
  optimisticDeleted: z.boolean().optional(),
  crossPosts: z
    .array(
      z.object({
        apId: z.string(),
        communitySlug,
      }),
    )
    .nullable(),
  myVote: z.number().optional(),
  optimisticMyVote: z.number().optional(),
  featuredCommunity: z.boolean(),
  optimisticFeaturedCommunity: z.boolean().optional(),
  featuredLocal: z.boolean(),
  optimisticFeaturedLocal: z.boolean().optional(),
  read: z.boolean(),
  optimisticRead: z.boolean().optional(),
  saved: z.boolean(),
  optimisticSaved: z.boolean().optional(),
});

// These internal representations of e.g. posts are defined via zod so we can validate the actual data if we want to.

// The example I've given is for post, but I plan on doing the same for person, community, comment, and maybe more

It's still a work in progress, but you can view the api adapter on this branch.

Clone this wiki locally