Gathering detailed insights and metrics for @ts-ghost/core-api
Gathering detailed insights and metrics for @ts-ghost/core-api
Gathering detailed insights and metrics for @ts-ghost/core-api
Gathering detailed insights and metrics for @ts-ghost/core-api
A collection of TypeScript packages and tools to interact with a ghost blog. Type-safe 🦾 Ghost Content API client and CLI 🤖 tool to export posts in Markdown.
npm install @ts-ghost/core-api
Typescript
Module System
Node Version
NPM Version
fix(admin-api): lexical output for pages
Updated on Jul 03, 2024
@ts-ghost/admin-api@4.2.0
Updated on Jun 06, 2024
@ts-ghost/content-api@4.1.0
Updated on Jun 06, 2024
@ts-ghost/core-api@6.1.0
Updated on Jun 06, 2024
@ts-ghost/ghost-blog-buster@0.6.16
Updated on May 07, 2024
@ts-ghost/core-api@6.0.0
Updated on Feb 24, 2024
TypeScript (96.35%)
Handlebars (2.84%)
JavaScript (0.81%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
50 Stars
291 Commits
4 Forks
2 Watchers
16 Branches
4 Contributors
Updated on May 24, 2025
Latest Version
6.1.0
Package Id
@ts-ghost/core-api@6.1.0
Unpacked Size
499.17 kB
Size
69.63 kB
File Count
9
NPM Version
10.2.4
Node Version
18.19.1
Published on
Jun 06, 2024
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
@ts-ghost/core-api
@ts-ghost/core-api
is a building block used by the `@ts-ghost/content-api` it contains the Type-safe logic of Query Builder and Fetchers.
@ts-ghost/core-api
contains the core building blocks for the @ts-ghost/content-api
package. It contains the Type-safe logic of Query Builder and Fetchers. Unless you are building a new package for @ts-ghost
you should not need to use this package directly.
1pnpm i @ts-ghost/core-api
This client is only compatible with Ghost versions 5.x for now.
Ghost 5^
Node.js 16+
fetch
being available, so you can bring your own
polyfill and if you run Node 16, you'll need to run with the
--experimental-fetch
flag enabled.The APIComposer is a class that helps you build the target API avec the available methods for a resource based on a combinations of ZodSchema. This APIComposer exposes 5 methods:
read
to fetch a single record andbrowse
to fetch multiple records.add
to create a record.edit
to update a record.delete
to delete a record.All these methods like read
and browse
gives you back the appropriate Fetcher
instance that will handle the actual request to the API with the correct parameters.
APIComposer
will handle type-safety of the query parameters and will return the appropriate fetcher and will pass along the correct output type based on the ZodSchema
you instantiate it with. For the query methods like browse
and read
, this output schema will be modified if required when you select specific fields, includes etc.
1import { z } from "zod"; 2import { APIComposer, type ContentAPICredentials } from "@ts-ghost/core-api"; 3 4const api: ContentAPICredentials = { 5 url: "https://ghost.org", 6 key: "7d2d15d7338526d43c2fadc47c", 7 version: "v5.0", 8 resource: "posts", 9}; 10 11const simplifiedSchema = z.object({ 12 title: z.string(), 13 slug: z.string(), 14 count: z.number().optional(), 15}); 16 17// the "identity" schema is used to validate the inputs of the `read`method of the APIComposer 18const identitySchema = z.union([z.object({ slug: z.string() }), z.object({ id: z.string() })]); 19 20// the "include" schema is used to validate the "include" parameters of the API call 21// it is specific to the Ghost API resource targeted. 22// The format is always { 'name_of_the_field': true } 23const simplifiedIncludeSchema = z.object({ 24 count: z.literal(true).optional(), 25}); 26 27const createSchema = z.object({ 28 foo: z.string(), 29 bar: z.string().nullish(), 30 baz: z.boolean().nullish(), 31}); 32 33const composedAPI = new APIComposer( 34 { 35 schema: simplifiedSchema, 36 identitySchema: identitySchema, 37 include: simplifiedIncludeSchema, 38 createSchema: createSchema, 39 createOptionsSchema: z.object({ 40 option_1: z.boolean(), 41 }), 42 }, 43 api 44);
identitySchema
can be any ZodType
and can also be an empty z.object({})
if you don't need the read
method.include
is a ZodObject
that will validate the include
parameters of the API call. It is specific to the Ghost API resource targeted. The format is always { 'name_of_the_field': true }
createSchema
(Optional) is a Zod Schema that will validate the input of the add
method of the APIComposer.
add
will take exactly the schema to parsecreateOptionsSchema
(Optional) is a Zod Schema that will validate options that are going to be passed as query parameters to the POST
url.updateSchema
(Optional) is a Zod Schema that will validate the input of the edit
method of the APIComposer.
edit
will fallback to a ZodPartial
(all fields are optional) version of the createSchema
if updateSchema
is not provided.After instantiation you can use the APIComposer
to build your queries with 2 available methods.
The browse
and read
methods accept a config object with 2 properties: input
and an output
. These params mimic the way Ghost API Content is built but with the power of Zod and TypeScript they are type-safe here.
1import { z } from "zod"; 2import { APIComposer, type ContentAPICredentials } from "@ts-ghost/core-api"; 3 4const api: ContentAPICredentials = { 5 url: "https://ghost.org", 6 key: "7d2d15d7338526d43c2fadc47c", 7 version: "v5.0", 8 resource: "posts", 9}; 10 11const simplifiedSchema = z.object({ 12 title: z.string(), 13 slug: z.string(), 14 count: z.number().optional(), 15}); 16const identitySchema = z.union([z.object({ slug: z.string() }), z.object({ id: z.string() })]); 17const simplifiedIncludeSchema = z.object({ 18 count: z.literal(true).optional(), 19}); 20 21const composedAPI = new APIComposer( 22 { schema: simplifiedSchema, identitySchema: identitySchema, include: simplifiedIncludeSchema }, 23 api 24); 25let query = composedAPI.browse({ 26 limit: 5, 27 order: "title DESC", 28 // ^? the text here will throw a TypeScript lint error if you use unknown field. 29 // In that case `title` is correctly defined in the `simplifiedSchema 30});
page
, limit
, order
, filter
. And read parameters are id
or slug
..browse
optionsInput are totally optionals on the browse
method but they let you filter and order your search.
This is an example containing all the available keys in the input
object
1const composedAPI = new APIComposer(
2 { schema: simplifiedSchema, identitySchema: identitySchema, include: simplifiedIncludeSchema },
3 api
4);
5let query = composedAPI.browse({
6 page: 1,
7 limit: 5,
8 filter: "title:typescript+slug:-test",
9 order: "title DESC",
10});
These browse params are then parsed through a Zod
Schema that will validate all the fields.
page:number
The current page requestedlimit:number
Between 0 and 15 (limitation of the Ghost API)filter:string
Contains the filter with Ghost API filter
syntax.order:string
Contains the name of the field and the order ASC
or DESC
.For the order
and filter
if you use fields that are not present on the schema (for example name
on a Post
) then the APIComposer will throw an Error with message containing the unknown field.
.read
optionsRead is meant to be used to fetch 1 object only by id
or slug
.
1const composedAPI = new APIComposer(
2 { schema: simplifiedSchema, identitySchema: identitySchema, include: simplifiedIncludeSchema },
3 api
4);
5let query = composedAPI.read({
6 id: "edHks74hdKqhs34izzahd45"
7});
8
9// or
10
11let query = composedAPI.read({
12 slug: "typescript-is-awesome-in-2025"
13});
You can submit both id
and slug
, but the fetcher will then chose the id
in priority if present to make the final URL query to the Ghost API.
If the parsing went okay, the read
and browse
methods from the APIComposer
will return the associated Fetcher
.
BrowseFetcher
for the browse
methodReadFetcher
for the read
methodBasicFetcher
is a special case when you don't need a APIComposer at all and want to fetch directly.Fetchers are instatiated automatically after using read
or browse
but these Fetchers can also be instantiated in isolation, in a similar way as the APIComposer with a config
containing the same schemas. But also a set of params
necessary to build the URL to the Ghost API.
1import { BrowseFetcher } from "@ts-ghost/core-api";
2
3// Example of instantiating a Fetcher, even though you will probably not do it
4const browseFetcher = new BrowseFetcher(
5 {
6 schema: simplifiedSchema,
7 output: simplifiedSchema,
8 include: simplifiedIncludeSchema,
9 },
10 {
11 browseParams: {
12 limit: 1,
13 },
14 },
15 api
16);
The option output
schema will be modified along the way after the params like fields
, formats
, include
are added to the query. At instantiation it will most likely be the same as the original schema.
These fetchers have a fetch
method that will return a discriminated union of 2 types:
1const composedAPI = new APIComposer( 2 { schema: simplifiedSchema, output: simplifiedSchema, include: simplifiedIncludeSchema }, 3 api 4); 5const readFetcher = composedAPI.read({ slug: "typescript-is-cool" }); 6let result = await readFetcher.fetch(); 7if (result.success) { 8 const post = result.data; 9 // ^? type {"slug":string; "title": string} 10} else { 11 // errors array of objects 12 console.log(result.errors.map((e) => e.message).join("\n")); 13}
After using .read
query, you will get a ReadFetcher
with an async fetch
method giving you a discriminated union of 2 types:
1// example for the read query (the data is an object) 2const result: { 3 status: true; 4 data: z.infer<typeof simplifiedSchema>; // parsed by the Zod Schema and modified by the fields selected 5} | { 6 status: false; 7 errors: { 8 message: string; 9 type: string; 10 }[]; 11}
After using .read
query, you will get a BrowseFetcher
with 2 methods:
async fetch
async paginate
.fetch()
That result is a discriminated union of 2 types:
1// example for the browse query (the data is an array of objects) 2const result: { 3 success: true; 4 data: z.infer<typeof simplifiedSchema>[]; 5 meta: { 6 pagination: { 7 pages: number; 8 limit: number; 9 page: number; 10 total: number; 11 prev: number | null; 12 next: number | null; 13 }; 14 }; 15} | { 16 success: false; 17 errors: { 18 message: string; 19 type: string; 20 }[]; 21}
.paginate()
1const result: { 2 success: true; 3 data: z.infer<typeof simplifiedSchema>[]; 4 meta: { 5 pagination: { 6 pages: number; 7 limit: number; 8 page: number; 9 total: number; 10 prev: number | null; 11 next: number | null; 12 }; 13 }; 14 next: BrowseFetcher | undefined; // the next page fetcher if it is defined 15} | { 16 success: false; 17 errors: { 18 message: string; 19 type: string; 20 }[]; 21 next: undefined; // the next page fetcher is undefined here 22}
Here you can use the next
property to get the next page fetcher if it is defined.
Output can be modified on the BrowseFetcher
and the ReadFetcher
through available methods:
.fields
.formats
.include
.fields()
The fields
methods lets you change the output of the result to have only your selected fields, it works by giving the property key and the value true
to the field you want to keep. Under the hood it will use the zod.pick
method to pick only the fields you want.
1import { BrowseFetcher } from "@ts-ghost/core-api"; 2 3// Example of instantiating a Fetcher, even though you will probably not do it 4const browseFetcher = new BrowseFetcher( 5 { 6 schema: simplifiedSchema, 7 output: simplifiedSchema, 8 include: simplifiedIncludeSchema, 9 }, 10 { 11 browseParams: { 12 limit: 1, 13 }, 14 }, 15 api 16); 17let result = await browseFetcher 18 .fields({ 19 slug: true, 20 title: true, 21 // ^? available fields come form the `simplifiedSchema` passed in the constructor 22 }) 23 .fetch(); 24 25if (result.success) { 26 const post = result.data; 27 // ^? type {"slug":string; "title": string} 28}
The output schema will be modified to only have the fields you selected and TypeScript will pick up on that to warn you if you access non-existing fields.
include
The include
method lets you include some additionnal data that the Ghost API doesn't give you by default. This include
key is specific to each resource and is defined in the Schema
of the resource. You will have to let TypeScript guide you to know what you can include.
1const bf = new BrowseFetcher(
2 { schema: simplifiedSchema, output: simplifiedSchema, include: simplifiedIncludeSchema },
3 {},
4 api
5);
6let result = await bf
7 .include({
8 count: true,
9 })
10 .fetch();
The output type will be modified to make the fields you include non-optionals.
formats
The formats
method lets you include some additionnal formats that the Ghost API doesn't give you by default. This is used on the Post
and Page
resource to retrieve the content in plaintext, html, or mobiledoc format. The available keys are html | mobiledoc | plaintext
and the value is a boolean to indicate if you want to include it or not.
1const bf = new BrowseFetcher(
2 { schema: simplifiedSchema, output: simplifiedSchema, include: simplifiedIncludeSchema },
3 {},
4 api
5);
6let result = await bf
7 .formats({
8 html: true,
9 plaintext: true,
10 })
11 .fetch();
The output type will be modified to make the fields html
and plaintext
non-optionals.
You can chain the methods to select the fields, formats, and include you want.
1const bf = new BrowseFetcher( 2 { schema: simplifiedSchema, output: simplifiedSchema, include: simplifiedIncludeSchema }, 3 {}, 4 api 5); 6let result = await bf 7 .fields({ 8 slug: true, 9 title: true, 10 html: true, 11 plaintext: true, 12 count: true, 13 }) 14 .formats({ 15 html: true, 16 plaintext: true, 17 }) 18 .include({ 19 count: true, 20 }) 21 .fetch();
fetch
optionsYou can pass an optional options
object to the fetch
and paginate
method. The options
object is the standard RequestInit
object from the fetch
API.
1let result = await api.posts.read({ slug: "typescript-is-cool" }).fetch({ cache: "no-store" });
This may be useful if you use NextJS augmented fetch
!
These mutations are async methods, they will return a Promise
that will resolve to the parsed result.
1const composedAPI = new APIComposer(
2 {
3 schema: simplifiedSchema,
4 identitySchema: identitySchema,
5 include: simplifiedIncludeSchema,
6 createSchema: createSchema,
7 createOptionsSchema: z.object({
8 option_1: z.boolean(),
9 }),
10 },
11 api
12);
13let newPost = await composedAPI.add(
14 {
15 title: "My new post",
16 },
17 {
18 option_1: true,
19 }
20);
input
object that will be parsed and typed with the createSchema
schema.options
object that will be parsed and typed with the createOptionsSchema
schema.The result will be parsed and typed with the output
schema and represent the newly created record.
1// return from the `add` method 2const result: { 3 success: true; 4 data: z.infer<typeof simplifiedSchema>; // parsed by the Zod Schema given in the config 5} | { 6 success: false; 7 errors: { 8 message: string; 9 type: string; 10 }[]; 11}
Edit requires the id
of the record to edit.
1let newPost = await composedAPI.edit("edHks74hdKqhs34izzahd45", { 2 title: "My new post", 3});
The result will be parsed and typed with the output
schema and represent the updated record.
id
of the record to edit.input
object that will be parsed and typed with the createSchema
schema wrapped with Partial. So all fields are optional.1// return from the `edit` method 2const result: { 3 success: true; 4 data: z.infer<typeof simplifiedSchema>; // parsed by the Zod Schema given in the config 5} | { 6 success: false; 7 errors: { 8 message: string; 9 type: string; 10 }[]; 11}
Delete requires the id
of the record to delete.
1let newPost = await composedAPI.edit("edHks74hdKqhs34izzahd45", { 2 title: "My new post", 3});
id
of the record to delete.The response will not contain any data since Ghost API just return a 204 empty response. You will have to check the discriminator success
to know if the deletion was successful or not.
1// return from the `delete` method 2const result: { 3 success: true; 4} | { 5 success: false; 6 errors: { 7 message: string; 8 type: string; 9 }[]; 10}
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
Distributed under the MIT License. See LICENSE for more information.
No vulnerabilities found.
No security vulnerabilities found.