#content collection directory location

26 messages · Page 1 of 1 (latest)

hexed sphinx
#

what directory should content collections be placed in relative to /src?

slow ruinBOT
#

Hi @hexed sphinx. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

  • With the new Content Layer API (Astro v5), collections can live anywhere in your project; you define them in src/content.config.ts and point a loader (e.g., glob or file) at their location. They are not required to be under src/content/. Also, collections with a loader are forbidden in src/content/ when legacy mode is enabled (Content collections guide and Built-in loaders; Legacy flags).

  • Legacy collections (v2–v4, type: 'content' or 'data') must be in src/content/. If you enable legacy.collections: true, those legacy collections continue to work only in src/content/ (Upgrade to v5 – Legacy; Legacy flags).

  • Starlight specifically expects docs and i18n content under src/content/docs/ and src/content/i18n/ via its loaders (Starlight project structure; Configure content collections).


Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hexed sphinx Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hexed sphinx
#

how would i define a loader and schema for

export interface Service {
    id: string;
    title: string;
    description: string;
    duration: number;
    price: number;
    category: string;
    type?: string;
    isOnline?: boolean;
    isInPerson?: boolean;
    image?: string;
  }

in a services.json file like

[
  {
    "id": "service-1",
    "title": "Initial Ayurvedic Consultation",
    "description": "A comprehensive assessment of your constitution (prakruti), current imbalances (vikruti), and personalized wellness recommendations.",
    "duration": 90,
    "price": 150,
    "isOnline": true,
    "isInPerson": false,
    "type": "ayurvedic",
    "image": "https://images.unsplash.com/photo-1649972904349-6e44c42644a7"
  },
  {
    "id": "service-2",
    "title": "Follow-up Consultation",
    "description": "Review your progress, address questions, and adjust recommendations as needed for your ongoing wellness journey.",
    "duration": 45,
    "price": 75,
    "isOnline": true,
    "isInPerson": false,
    "type": "ayurvedic"
  },
  {
    "id": "service-3",
    "title": "Private Yoga Consultation",
    "description": "Personalized therapeutic yoga practice designed specifically for your health concerns and wellness goals.",
    "duration": 75,
    "price": 130,
    "isOnline": true,
    "isInPerson": false,
    "type": "yoga",
    "image": "https://images.unsplash.com/photo-1518495973542-4542c06a5843"
  },
  {
    "id": "service-5",
    "title": "Marma Therapy Session",
    "description": "Gentle pressure applied to specific vital energy points to release blockages and restore the flow of prana throughout the body.",
    "duration": 60,
    "price": 120,
    "isOnline": false,
    "isInPerson": true,
    "type": "marma",
    "image": "https://images.unsplash.com/photo-1721322800607-8c38375eef04"
  }
]
#

@slow ruin how would i define a loader and schema for

export interface Service {
    id: string;
    title: string;
    description: string;
    duration: number;
    price: number;
    category: string;
    type?: string;
    isOnline?: boolean;
    isInPerson?: boolean;
    image?: string;
  }

in a services.json file like

[
  {
    "id": "service-1",
    "title": "Initial Ayurvedic Consultation",
    "description": "A comprehensive assessment of your constitution (prakruti), current imbalances (vikruti), and personalized wellness recommendations.",
    "duration": 90,
    "price": 150,
    "isOnline": true,
    "isInPerson": false,
    "type": "ayurvedic",
    "image": "https://images.unsplash.com/photo-1649972904349-6e44c42644a7"
  },
  {
    "id": "service-2",
    "title": "Follow-up Consultation",
    "description": "Review your progress, address questions, and adjust recommendations as needed for your ongoing wellness journey.",
    "duration": 45,
    "price": 75,
    "isOnline": true,
    "isInPerson": false,
    "type": "ayurvedic"
  },
  {
    "id": "service-3",
    "title": "Private Yoga Consultation",
    "description": "Personalized therapeutic yoga practice designed specifically for your health concerns and wellness goals.",
    "duration": 75,
    "price": 130,
    "isOnline": true,
    "isInPerson": false,
    "type": "yoga",
    "image": "https://images.unsplash.com/photo-1518495973542-4542c06a5843"
  },
  {
    "id": "service-5",
    "title": "Marma Therapy Session",
    "description": "Gentle pressure applied to specific vital energy points to release blockages and restore the flow of prana throughout the body.",
    "duration": 60,
    "price": 120,
    "isOnline": false,
    "isInPerson": true,
    "type": "marma",
    "image": "https://images.unsplash.com/photo-1721322800607-8c38375eef04"
  }
]
slow ruinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You can use the Content Layer API with the built-in file() loader and a Zod schema. Create src/content.config.ts:

import { defineCollection, z } from 'astro:content';
import { file } from 'astro/loaders';

const services = defineCollection({
  loader: file('src/data/services.json'),
  schema: z.object({
    id: z.string(),
    title: z.string(),
    description: z.string(),
    duration: z.number(),
    price: z.number(),
    category: z.string().optional(), // not present in your JSON; make optional if not used
    type: z.string().optional(),
    isOnline: z.boolean().optional(),
    isInPerson: z.boolean().optional(),
    image: z.string().url().optional(),
  }),
});

export const collections = { services };

Notes:


Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hexed sphinx Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hexed sphinx
#

@slow ruin i want the services.json to be pulled from the /content directory outside of /src so it's not exposed to the client?

slow ruinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes. With Astro’s Content Layer API, you can load data from anywhere in your project, not just under src. Point the loader to your external path (e.g., ./content/services.json), and it won’t be bundled for the client—only the validated data you query is used at build time.

Example src/content.config.ts:

import { defineCollection, z } from 'astro:content';
import { file } from 'astro/loaders';

const services = defineCollection({
  loader: file('./content/services.json'),
  schema: z.object({
    id: z.string(),
    title: z.string(),
    description: z.string(),
    duration: z.number(),
    price: z.number(),
    category: z.string().optional(),
    type: z.string().optional(),
    isOnline: z.boolean().optional(),
    isInPerson: z.boolean().optional(),
    image: z.string().url().optional(),
  }),
});

export const collections = { services };
#

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hexed sphinx Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hexed sphinx
#

@slow ruin great, is the a way to sync corresponding events in Cal.com when new services are pushed into the collection?

const url = 'https://api.cal.com/v2/event-types';
const options = {
  method: 'POST',
  headers: {
    'cal-api-version': '<cal-api-version>',
    Authorization: '<authorization>',
    'Content-Type': 'application/json'
  },
  body: '{}'
};
const url = 'https://api.cal.com/v2/event-types/{eventTypeId}';
const options = {
  method: 'PATCH',
  headers: {
    'cal-api-version': '<cal-api-version>',
    Authorization: '<authorization>',
    'Content-Type': 'application/json'
  },
  body: '{}'
};
const url = 'https://api.cal.com/v2/event-types/{eventTypeId}';
const options = {
  method: 'DELETE',
  headers: {'cal-api-version': '<cal-api-version>', Authorization: '<authorization>'},
  body: undefined
};
const url = 'https://api.cal.com/v2/event-types';
const options = {
  method: 'GET',
  headers: {'cal-api-version': '<cal-api-version>'},
  body: undefined
};
const url = 'https://api.cal.com/v2/event-types/{eventTypeId}';
const options = {
  method: 'GET',
  headers: {'cal-api-version': '<cal-api-version>', Authorization: '<authorization>'},
  body: undefined
};
slow ruinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I’m sorry, but the knowledge sources don’t include any guidance on syncing a content collection with an external API like Cal.com or triggering web requests on collection changes. They describe how to define loaders and schemas, query collections, and use the Content Loader API, but not automations to create/update/delete external records when entries change (Content collections; Defining Collections; Loader types; LoaderContext and DataStore and).

What is documented:

  • Content loaders run at build time; collections are updated during builds, not in real time. For live updates you’d use other fetching methods or on-demand rendering, not collections (When not to create a collection).
  • You can build a custom loader that pulls data in, but the docs don’t specify pushing changes out to third-party APIs as part of the loader lifecycle (Building a custom loader).
  • Deployments for CMS workflows can use webhooks to trigger rebuilds, but this is about rebuilding the site, not syncing to external services (Publishing your site → Rebuild on changes).
#

Given the docs, there isn’t an official, documented mechanism to push to Cal.com on collection changes. You would need to implement your own build-time or external process.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hexed sphinx Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hexed sphinx
#

@slow ruin great, is the a way to sync corresponding events in Cal.com at build time after new services are pushed into the collection?

const url = 'https://api.cal.com/v2/event-types';
const options = {
  method: 'POST',
  headers: {
    'cal-api-version': '<cal-api-version>',
    Authorization: '<authorization>',
    'Content-Type': 'application/json'
  },
  body: '{}'
};
const url = 'https://api.cal.com/v2/event-types/{eventTypeId}';
const options = {
  method: 'PATCH',
  headers: {
    'cal-api-version': '<cal-api-version>',
    Authorization: '<authorization>',
    'Content-Type': 'application/json'
  },
  body: '{}'
};
const url = 'https://api.cal.com/v2/event-types/{eventTypeId}';
const options = {
  method: 'DELETE',
  headers: {'cal-api-version': '<cal-api-version>', Authorization: '<authorization>'},
  body: undefined
};
const url = 'https://api.cal.com/v2/event-types';
const options = {
  method: 'GET',
  headers: {'cal-api-version': '<cal-api-version>'},
  body: undefined
};
const url = 'https://api.cal.com/v2/event-types/{eventTypeId}';
const options = {
  method: 'GET',
  headers: {'cal-api-version': '<cal-api-version>', Authorization: '<authorization>'},
  body: undefined
};
slow ruinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I’m sorry, but the knowledge sources don’t document any built-in way to sync content collection changes to an external API (like Cal.com) at build time. The docs cover defining loaders/schemas and running build/development commands, but not pushing changes out to third-party services during the build lifecycle (Content collections; Loader types; astro dev hotkeys; astro sync).

If you want to attempt this, the closest documented extension point is writing an Astro integration that runs a custom hook during the build (e.g., in astro:build:setup) where you could call external APIs, but the docs don’t provide a pattern specifically for syncing collections to external services (Building your own integration; astro:build:setup hook; Hooks overview; Custom hooks).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@hexed sphinx Kapa.ai is still learning and improving, please let me know how I did by reacting below.

hexed sphinx
#

@slow ruin what about astro:server:setup which hook is best suited for syncing external objects with local content like services.json? services.json is the source of truth, external services like cal.com need to be updated to follow content defined in services.json

slow ruinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Short answer: use an Astro integration and run your sync during the build lifecycle, not the dev server hooks.

  • astro:server:setup is for dev-mode server middleware and dev toolbar messaging. It’s called only in astro dev before listen(), and is intended for Vite server customization and content refresh during development, not for production build syncs (e.g., Cal.com) (astro:server:setup). It also provides refreshContent for dev-only content updates (refreshContent() option).

  • For build-time syncing (services.json → Cal.com), prefer a build hook:

    • astro:build:start or astro:build:setup to prepare and read your content, then call external APIs (astro:build:setup, Hooks).
    • If you need the fully generated output first (rare), astro:build:generated/astro:build:done run after generation; you could still call APIs there, but docs recommend astro:build:done unless you specifically need pre-cleanup access (astro:build:generated).

Given your goal, implement the sync in an integration’s astro:build:setup or astro:build:done. Avoid astro:server:setup for this purpose.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: