question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

RFC: @prismicio/client Refresh

See original GitHub issue

@prismicio/client Refresh

While using @prismicio/client during gatsby-source-prismic’s development and testing direct usage in Next.js, I came across areas where the library could be improved. Functionally, the library works well and does everything I needed from it. Focusing on a simpler and more explicit API and taking advantage of the user’s environment (e.g. their browser or framework), however, could make for a better developer experience.

Some of the following notes have been explored in ts-prismic, a library created to address issues I encountered.

While ts-prismic focuses on providing a minimal interface to the REST API via URL builders and TypeScript types, the @prismicio/client library would need to include more functionality. ts-prismic can be used as a basis to rebuild most of the existing @prismicio/client API with a lighter codebase. Code from ts-prismic would be moved into the library, not installed as a dependency.

This refresh would need to have complete feature parity with the current API. If a function is removed, a straightforward replacement should be available which may be a direct function replacement or consist of a series of other functions.

Happy to get feedback on these ideas to see how we can make this library even easier to use and more connected to users’ codebases.

Goals

  • Simpler API with less overlap between functions
  • Strong TypeScript support
  • Type-safety at compiler and run time
  • Configurable network requests
  • Smaller bundle
  • Easy upgrade path
  • Support modern environments

What the library does right

  • Easy to create a client and get started Users don’t need to know how the REST API works. Just create a client with Prismic.client and get a document with client.getByID.

  • The standard API in JavaScript projects Documentation is plentiful as it can be found in several guides. Googling a problem will likely result in some kind of code example.

  • Abstracts common use cases Predicates can be created using easy-to-use functions with built-in JavaScript types like numbers and Date. Preview URLs can be resolved by providing just the preview token, document ID, and Link Resolver without needing to know what happens in the background.

Where the library could be cleaned-up

  • Bloaded API Overlap between methods can cause confusion.

    // Which one am I supposed to use?
    
    const client = Prismic.client("...");
    const api = Prismic.api("...");
    const api2 = Prismic.getApi("...");
    
  • Platform-specific methods Core functions like Prismic.client contain options for Node.js-specific code, like req. Should a generic JavaScript library include Node.js-specific code? If yes, should it be more explicit about its integration?

  • TypeScript support The library is written in TypeScript, but is not written or exported for library users.

    import { Document } from "@prismicio/client/d.ts/documents";
    import ResolvedApi from "@prismicio/client/d.ts/ResolvedApi";
    
    // Compared to:
    
    import { Document, Client } from "@prismicio/client";
    
  • File size 6.9kb gzipped, partially due to its inclusion of cross-fetch. cross-fetch might be unavoidable if we want a super simple setup for Node.js users, but we could probably reduce the library code size.

  • Legacy API styles Promises are supported nearly everywhere. Usage with async/await makes it even easier and can be transpiled down to earlier ES version if necessary. Callback-style functions could be dropped and Promise-based APIs embraced everywhere.

New features

  • Expose URL builders URL builders could be provided that create URLs that the client would use. This allows users to build URLs for cases where a full client may not be desired, such as when you are unable to pass around object references.

  • User-facing TypeScript types Export types to help users provide type-safety in their projects. This includes types like Document or Ref. These types may or may not be used internally by the library, but they can still be provided to users since their code may need it.

    These types should be flexible and extendable through generics and interfaces.

  • Runtime type guards Export type guard functions for common use cases like Documents and fields types (e.g. isDocument, isRichTextField).

  • Maybe: Runtime type guards based on Custom Type JSON schemas Runtime type guards could be created using Custom Type JSON schemas. The JSON file could generate a function that checks each field using something like io-ts. This would require the full JSON schemas to be available at runtime and could make for a very large bundle. This may not be worth the download cost if field-specific type guards are available (the feature described above).

  • Provide the ability to have a custom fetcher When creating a client, a user could provide a function to perform network requests that the client would make. This function could hook into caching and use built-in functions depeneding on the user’s environment, like the browser’s fetch function.

  • Documentation within the library via TSDoc Documentation for the library’s functions and types could be included within the library. As developers are using the library, they can view documentation (which may link to more in-depth docs) without needing to change contexts. Documentation for functions, parameters, and return types could be presented as users are typing.

What this library could look like

Creating a client and querying documents:

import * as prismic from '@prismicio/client'
import got from 'got'

const endpoint = prismic.defaultEndpoint('qwerty')
const client = prismic.createClient(endpoint, {
  accessToken: process.env.PRISMIC_ACCESS_TOKEN,
  fetcher: (url) => got.json(url),
})

const document = await client.getByID('abc123')
const response = await client.query([
  prismic.predicate.at('document.type', 'blog-post'),
  prismic.predicate.has('my.blog-post.title'),
])

Using a type guard at runtime and compile-time:

import * as prismic from '@prismicio/client'

const endpoint = prismic.defaultEndpoint('qwerty')
const client = prismic.createClient(endpoint, {
  accessToken: process.env.PRISMIC_ACCESS_TOKEN,
  fetcher: (url) => got.json(url),
})

const document = await client.getByID('abc123')

if (prismic.isRichTextField(document.data.description)) {
  console.log('document.data.description is now typed as prismic.StructuredText')
}

User-facing TypeScript types:

import * as prismic from 'ts-prismic'

// Available as prismic.Document
export interface Document<Data = Record<string, unknown>> {
  id: string
  uid?: string
  url?: string
  type: string
  href: string
  tags: string[]
  slugs: string[]
  lang?: string
  alternate_languages: AlternateLanguage[]
  first_publication_date: string | null
  last_publication_date: string | null
  data: Data
}

// Available as prismic.Query
export interface Query<TResults extends Document[] = Document[]> {
  page: number
  results_per_page: number
  results_size: number
  total_results_size: number
  total_pages: number
  next_page: string | null
  prev_page: string | null
  results: TResults
}

Using URL builders directly without a client:

import * as prismic from 'ts-prismic'
import got from 'got'

// Get the repo's API endpoint
const endpoint = prismic.defaultEndpoint('qwerty')

// Get repo metadata
const repoUrl = prismic.buildRepositoryURL(endpoint)
const repo = await got(repoUrl).json<prismic.Response.Repository>()

// Get the master ref
const masterRef = repo.refs.find((ref) => ref.isMasterRef)

// Query all documents
const allDocsUrl = prismic.buildQueryURL(endpoint, masterRef.ref)
const allDocs = await got(allDocsUrl).json<prismic.Response.Query>()

Related issues

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:6
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
angeloashmorecommented, Apr 30, 2021

Thanks @Duaner!

Regarding Node.js req support, that makes sense. I’ve yet to read into the codebase, but I assume this automatically reads the preview cookie from the req anytime a query is made.

I agree that this process should be seamless without knowing how it’s working, but perhaps it could be a more transparent about what it’s doing.

Something like:

import * as prismic from '@prismicio/client'

const endpoint = prismic.defaultEndpoint('my-repo')
const client = prismic.createClient(endpoint)

// This could set internal state to use the browser's cookie by default on queries. Enabled by default.
client.enableAutomaticPreview()

// This could be called immediately after client creation or within a request handler where `req` is available.
// This means the client can be created outside the request handler.
// For Node, this explicit call is necessary for preview support. It says what it will do.
client.enableAutomaticPreview(req)

// This could disable any automatic preview ref usage (browser cookie or `req`)
client.disableAutomaticPreview()

// For anything other than document.cookie or req.headers.cookie, a static value could be provided.
// For custom functionality like this, knowing about the "ref" concept is expected.
// For example, this could read some context value from elsewhere when a query request is made
client.withRef('my-ref')

// The ref is determined based on the previous function calls.
// It would be the master ref by default, or the preview cookie if one is available.
const document = await client.getByID('abc123')

I’ll have to think more about the actual API and names. But overall the goal is to provide more clarity on how the req parameter is being used directly with the library without needing to read auxiliary documentation. Also to keep non-Node.js users in mind and making it as straightforward to use.

1reaction
lihbrcommented, Jan 5, 2022

Shipped 🎉

Read more comments on GitHub >

github_iconTop Results From Across the Web

RFC: @prismicio/client Refresh · Issue #163 - GitHub
The library is written in TypeScript, but is not written or exported for library users. import { Document } from "@prismicio/client ...
Read more >
Next.js Preview using @prismicio/client@^6.0.0 - Prismic People
I hit preview, it opens my localhost, it goes to the page, it shows the loader, then it seems to refresh and error....
Read more >
prismicio - Bountysource
Hi Prismic folks :) I'm trying to use the nodejs package with its bundled d.ts files. However, types are not properly imported when...
Read more >
Introduction - @prismicio/vue
Join the conversation in the ongoing Vue 3 Kit RFC and Suggestion Thread! ... @prismicio/vue is a Vue.js 3 plugin that helps you...
Read more >
@prismicio/client - npm
The official JavaScript + TypeScript client library for Prismic. Latest version: 6.7.3, last published: 6 days ago.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found