@flotiq/flotiq-api-sdk
TypeScript icon, indicating that this package has built-in type declarations

1.0.0-alpha • Public • Published

Flotiq API Javascript/Typescript SDK

Dedicated js/ts client for Flotiq Headless CMS API which focuses on type safety and IDE autocompletion of user data types.

With types generated using our typegen command, it enables fast and typesafe development with Flotiq as a data backend.

Generated flotiq-api.d.ts types can be either committed with your code, or .gitignore-d and generated during development and CI/CD.

You can still use all the API features without type generation. TypeScript user types can be added or removed at any point in development without code changes required.

Note: This SDK is stable and actively maintained. It is production-ready as of version 1.0.0.
New features and improvements are still planned — see Development Status for more.

Table of contents:

Features

JavaScript Typescript
Fetch and update Flotiq content
Search API
TS/JS types for content definitions in data ✅* ✅**
TS/JS types for content definitions in data with hydrated relations ✅* ✅**
TS/JS types for content definitions in filters ✅* ✅**
Discriminating between data types in any type relations and search responses ✅*** ✅***
Custom Middleware (e.g. to add next cache to each fetch)
Generating URL to uploaded Media
CTD operations
  • * Requires typegen types and <reference path="./flotiq-api.d.ts" /> annotation
  • ** Requires typegen types
  • *** Requires typegen types, only with api.helpers.instanceOf helper

Installation from npm

  1. run npm install @flotiq/flotiq-api-sdk
  2. (optional) Generate typescript types for your data definitions
    1. Create .env file and add FLOTIQ_API_KEY env variable inside
    2. Run npm exec flotiq-api-typegen to generate account type definitions

Installation from yarn

  1. run yarn add @flotiq/flotiq-api-sdk
  2. (optional) Generate typescript types for your data definitions
  3. Create .env file and add FLOTIQ_API_KEY env variable inside
  4. Run yarn exec flotiq-api-typegen to generate account type definitions

Usage examples

In JS (CommonJS)

// Only if types were generated:
/// <reference path="./flotiq-api.d.ts" />

const { Flotiq } = require("@flotiq/flotiq-api-sdk");

const api = new Flotiq(); // read api key automatically from process.env.FLOTIQ_API_KEY
// or
const api = new Flotiq({
  apiKey: "<YOUR API KEY>",
});

await api.content._media.list().then((response) => {
  console.log("media > list", response);
});

In Typescript

import { Flotiq } from "@flotiq/flotiq-api-sdk";

const api = new Flotiq(); // read api key automatically from process.env.FLOTIQ_API_KEY
// or
const api = new Flotiq({
  apiKey: "<YOUR API KEY>",
});

await api.content._media.list().then((response) => {
  console.log("media > list", response);
});

List objects

api.content._media.list().then((response) => {
  console.log("media > list", response);
});

api.content._media
  .list({
    page: 1,
    limit: 100,
    orderBy: "fileName",
    orderDirection: "asc",
  })
  .then((response) => {
    console.log(
      "media > list (limit=100, page=1, orderBy=fileName,asc)",
      response
    );
  });

Get object

api.content._media.get("_media-xyz-id").then((response) => {
  console.log("media > get by id", response);
});

Hydrate objects

// works also on list()
api.content._media.get("_media-xyz-id", { hydrate: 1 }).then((response) => {
  console.log("media > get", response.tags);
});

Hydrate objects to second level

// works also on list()
api.content._media.get("_media-xyz-id", { hydrate: 2 }).then((response) => {
  console.log("media > get", response.tags);
});

Generate media url

// works also on list()
api.content._media.get("_media-xyz-id").then((response) => {
  console.log(
    "media > url",
    api.helpers.getMediaUrl(response, {
      width: 200,
      height: 200,
    })
  );
});

Filter Objects

api.content.blogpost
  .list({
    filters: {
      image: {
        type: "contains",
        filter: "_media",
      },
    },
  })
  .then((response) => {
    console.log("only blogposts with image", response);
  });

Create objects

api.content.blogpost
  .create({
    title: "New post 2",
  })
  .then((newPost) => console.log(newPost.id));

Iterate all pages

const params = {
  page: 1,
  limit: 100,
  orderBy: "fileName" as keyof InternalMedia,
  orderDirection: "asc" as "asc" | "desc",
};

let result: ListResponse<InternalMedia>;
do {
  result = await api.content._media.list(params);
  console.log(result.data[0].fileName);
  params.page++;
} while (result.total_pages > result.current_page);

Search API

api.search.query({ q: "asd" }).then((response) => {
  console.log(
    "search score for item 0",
    response.data[0].score,
    response.data[0].item.id
  );
});

Search API with type checking

api.search
  .query({ q: "The name of the thing" })
  // note added response type below:
  .then((response: SearchResponse<Blogpost | Product>) => {
    for (const searchResult of response.data) {
      if (api.helpers.instanceOf(searchResult.item, "blogpost")) {
        // firstResult.item is now Blogpost
        console.log("Found blogpost!", searchResult.item.title);
      } else if (api.helpers.instanceOf(searchResult.item, "product")) {
        // firstResult.item is now Product
        console.log("Found product!", searchResult.item.name);
      } else {
        // firstResult.item is neither Blogpost or Product
        // do nothing
      }
    }
  });

CTD Scoped Search API

api.content.blogpost.search({ q: "Product updates" }).then((response) => {
  const blogpost = response.data[0].item; // Typed as Blogpost!
  console.log("Typed search result", blogpost.title);
});

Middleware Intercepting all requests that go to flotiq api:

const api = new Flotiq({
  apiKey: "<YOUR API KEY>",
  middleware: [
    {
      beforeRequest: async (requestContext) => {
        console.log("Requesting:", requestContext.url);

        return {
          ...requestContext,
          init: {
            ...requestContext.init,
            headers: {
              ...requestContext.init.headers,
              "User-Agent": "My cool app",
            },
          },
        };
      },
    },
  ],
});

SDK API - Flotiq class

constructor

import { Flotiq } from "@flotiq/flotiq-api-sdk";

const api = new Flotiq(); 
//or
const api = new Flotiq(options); 

options argument can contain the following properties:

  • apiKey: string - your Flotiq api key. If none provided, process.env.FLOTIQ_API_KEY will be used.

  • apiUrl: string - Flotiq API url. By default, it points to https://api.flotiq.com. You'll need to change it only if you are Flotiq Enterprise client, with self-hosted Flotiq instance.

  • middleware: Array - an array of middleware objects intercepting all requests. If multiple middlewares are used, they will be executed in order provided in the array.

    • middleware[].beforeRequest - a callback intercepting each request before it happens. It can override all fetch options (including url) and fetch method itself. It must return new (or modified) context.
       {
         beforeRequest: (requestContext: {
           url: string;
           init: RequestInit;
           fetch: typeof fetch;
         }) => Promise<{
           url: string;
           init: RequestInit;
           fetch: typeof fetch;
         }>
       }

api.content.<content_definition_api_name>

You can access each content type data api using its api name.

Content api provides following methods:

  • list(listOptions) - used to list mulitple objects. For more infe see Flotiq Docs - Listing content through the API. listOptions can contain following properties:

    • hydrate: 0 | 1 | 2 - hydration level for relations. 0 will not include related objects, 1 will join data from related objects, 2 will join data from related objects and from their relations. Default value is 0.
    • filters: Object - object containing filtering rules
    • orderBy: string - which Content Type Definition property determines the order
    • orderDirection: "asc" | "desc" - order direction
    • limit: number - how many results should be returned in the result
    • page: number - page number. With limit: 10, page: 1 will return first 10 results, page: 2 - results 11-20 and so on.
  • get(id: string, options) - find single object by its id. options is an object with following properties:

    • hydrate: 0 | 1 | 2 - hydration level (see above)
  • update(id: string, data: Object) - Perform PUT operation on Content Type Object with provided data.

  • patch(id: string, data: Object) - Perform PATCH operation on Content Type Object with partial data.

  • delete(id: string) - remove single object.

  • create(data: Object) - create new object

  • search(searchParams) - search through all objects of given type with Full-text search API endpoint. (see detailed parameters below)

    Results are scoped to selected Content Type Definition, therefore the two following search queries are performing the same API call:

      api.content.<content_definition_api_name>.search({
        q: 'search string'
      })
       api.search.query({ 
         contentType: ["<content_definition_api_name>"], 
         q: 'search string' 
       })

api.search.query(searchParams)

Performs Full-text search operation with provided parameters. For more details see Flotiq docs - Search API.

searchParams accepts the following parameters:

  • orderBy: string - which field determines order of results
  • orderDirection: "asc" | "desc" - ordering direction
  • limit: number - number of results per page
  • page: number - page number
  • q: string - query string (required)
  • contentType: string[] - Limits results to provided content types. If not provided, Search API will search through all Content Type Definitions.

api.helpers

Helper methods to work with API and SDK itself. These are:

  • api.helpers.getMediaUrl(media: InternalMedia, options) - generate url to media file uploaded to Flotiq. options can contain:

    • width: number, height: number - for images, Flotiq will return resized image. If one of the dimensions is omitted, resize operation will preserve original image proportions.
    • variantName: string - for images, Flotiq will return variant of the image. Can be also resized. If variant is not present url fallbacks to original image
    • type: 'image'| 'file' - default: 'image', use it to change the url behaviour and return urls with '/file/' instead of '/image/widthxheight/'.
    • omitFileName: boolean - default: false, use it to change the url behaviour and return urls with '/ID.extension' instead of '/ID/fileName'.
  • api.helpers.instanceOf(data: object, ctdName: string) - checks if object belongs to provided CTD.

    When using generated Typescript types, it will determine which Content Type Definition interface is the object typed with. See Search API with type checking example for more detailed use case. It will also help determine hydrated relation data type.

flotiq-api-typegen

flotiq-api-typegen

Generates types for Flotiq API based on the content type definitions on your account

Options

Option Description Default
--flotiq-key Flotiq API key with read only access
--flotiq-api-url Url to the Flotiq API https://api.flotiq.com
--output Path to target filename ./flotiq-api.d.ts
--watch Watch for changes in the content type definitions and regenerate types false

Development

See CONTRIBUTING.md

Development Status

Features that might appear in the future (not necessarily before v1):

  • JS flavour support

    • [x] Typescript - ts files
    • [x] CommonJS - js with require
    • [ ] ECMAScript - js with import (esm build needs to be added, for now it may not work in all projects)
  • [x] typegen based on CTDs

    • [x] filter knows what filter flotiq have
    • [x] (partial) filter knows what fields user have (were missing internals for the moment, but own fields are listed)
    • [x] hydrate knows what are results for different hydration levels
    • [x] crud operations know param and result types
    • [x] filter knows it can only list
    • [x] hydrate knows it can only list and get
  • [x] basic get/list/update/create/delete objects

  • [x] filter

    • [x] smart filter types that know if they require filter and filter2 params
  • [x] hydration

    • [ ] default hydration: export const flotiq = new Flotiq({ hydrate: 1 })
  • [ ] search (wip)

    • [x] content type scoped search
    • [ ] all search params
    • [x] result type recognition
  • [ ] virtual hydration (e.g. after search)

  • [ ] batch operations

  • [x] ordering

  • [x] pagination parameters

  • [x] middlewares (e.g. for next)

    • [x] fetch override
  • [ ] CTD operations (list/create/get/update) - flotiq.definitions.list() etc

  • [x] api key autoloading from .env when creating Flotiq

  • [x] getMediaUrl

  • [ ] upload media api

  • [x] changelog

  • [ ] contributing guide (in progress)

  • [x] CLI params, help text and other DX features (yargs)

  • [ ] Namespace support for multiple spaces used

    • proposed api:

      const defaultApi = new Flotiq(); // Based on key provided as FLOTIQ_API_KEY
      const blogApi = new Flotiq.ns.Blog(); // Based on space name or provided alias && FLOTIQ_BLOG_API_KEY
      const pageApi = new Flotiq.ns.Page(); // Based on space name or provided alias && FLOTIQ_PAGE_API_KEY
      
      blogApi.content.blogpost... // typed ctds from blog space
      pageApi.content.menu...  // Typed ctds from page space
      
      pageApi.content.blogpost... // Not existing ctds in given space will not provide typing and throw an error 404 from backend
    • example data migration

    • example - cross space usage (e.g. next implementation of https://flotiq.com/blog/)

Readme

Keywords

none

Package Sidebar

Install

npm i @flotiq/flotiq-api-sdk

Weekly Downloads

110

Version

1.0.0-alpha

License

MIT

Unpacked Size

127 kB

Total Files

45

Last publish

Collaborators

  • flotiq